C# 線程池ThreadPool

什么是線程池?為什么要用線程池?怎么用線程池?

1. 什么是線程池?

??? ? ??.NET Framework的ThreadPool類提供一個線程池,該線程池可用于執行任務、發送工作項、處理異步 I/O、代表其他線程等待以及處理計時器。那么什么是線程池?線程池其實就是一個存放線程對象的“池子(pool)”,他提供了一些基本方法,如:設置pool中最小/最大線程數量、把要執行的方法排入隊列等等。ThreadPool是一個靜態類,因此可以直接使用,不用創建對象。

2. 為什么要用線程池?好處是什么?

??????? 微軟官網說法如下:許多應用程序創建大量處于睡眠狀態,等待事件發生的線程。還有許多線程可能會進入休眠狀態,這些線程只是為了定期喚醒以輪詢更改或更新的狀態信息。?線程池,使您可以通過由系統管理的工作線程池來更有效地使用線程。

??????? 說得簡單一點,每新建一個線程都需要占用內存空間和其他資源,而新建了那么多線程,有很多在休眠,或者在等待資源釋放;又有許多線程只是周期性的做一些小工作,如刷新數據等等,太浪費了,劃不來,實際編程中大量線程突發,然后在短時間內結束的情況很少見。于是,就提出了線程池的概念。線程池中的線程執行完指定的方法后并不會自動消除,而是以掛起狀態返回線程池,如果應用程序再次向線程池發出請求,那么處以掛起狀態的線程就會被激活并執行任務,而不會創建新線程,這就節約了很多開銷。只有當線程數達到最大線程數量,系統才會自動銷毀線程。因此,使用線程池可以避免大量的創建和銷毀的開支,具有更好的性能和穩定性,其次,開發人員把線程交給系統管理,可以集中精力處理其他任務。


3. 怎么使用線程池?

其實線程池使用起來很簡單,如下

a.設置線程池最大最小:

ThreadPool.SetMaxThreads (int workerThreads,int completionPortThreads)
設置可以同時處于活動狀態的線程池的請求數目。所有大于此數目的請求將保持排隊狀態,直到線程池線程變為可用。還可以設置最小線程數。

b.將任務添加進線程池:

ThreadPool.QueueUserWorkItem(new?WaitCallback(方法名));


ThreadPool.QueueUserWorkItem(new?WaitCallback(方法名), 參數);

舉個小例子,線程池中最多5個線程,執行一個方法60次,算5年總工資,如下:



如果不采用線程池,恐怕要開60線程異步執行Run()方法,空間資源之浪費,可見一斑。而現在我們最多用了5個線程,1秒內即可執行完畢,效率、性能都很好。

----------------------------------------------------------------------------------------------------------------------------------

C#線程池ThreadPool.QueueUserWorkItem接收線程執行的方法返回值

  最近在項目中需要用到多線程,考慮了一番,選擇了ThreadPool,我的需求是要拿到線程執行方法的返回值,

但是ThreadPool.QueueUserWorkItem的回調方法默認是沒有返回值的,搜了搜,都是簡單介紹ThreadPool.QueueUserWorkItem的各種

用法,只能自己想辦法了。

  回調方法不帶返回值,迂回一下,回調方法用對象的方法,返回值放在對象的屬性中,在對象方法執行時將需要的返回值賦值給對應屬性。

等所有線程執行完,循環對象列表,取回返回值,然后想怎么處理返回值就OK了。上代碼: 

  封裝對象:

復制代碼
 1 using System;
 2 using System.Threading;
 3  public class ThreadReturnData
 4     {
 5         public ManualResetEvent manual;
 6         public string res;
 7 
 8         public void ReturnThreadData(object obj)
 9         {
10             //線程耗時操作方法
11             res = DoSomething(obj);
12              manual.Set();
13         }
14     }
復制代碼

  多線程調用:

復制代碼
 1  List<ThreadReturnData> testList = new List<ThreadReturnData>();
 2 IList<ManualResetEvent> arrManual = new List<ManualResetEvent>();
 3 for (int i = 0; i < i; i++)
 4 {
 5                             ThreadReturnData temp = new ThreadReturnData();
 6                             temp.manual = new ManualResetEvent(false);
 7                             arrManual.Add(temp.manual);
 8                             ThreadPool.QueueUserWorkItem(new WaitCallback(temp.ReturnThreadData), i);
 9                             testList.Add(temp);
10                         }
11 }
12                     if (arrManual.Count > 0)
13                     {
14                         ////等待所有線程執行完
15                         WaitHandle.WaitAll(arrManual.ToArray());
16                     }
17  foreach (ThreadReturnData d in testList)       
18  {
19 d.res;
20 //todo
21 }
復制代碼
-------------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------------------------------

https://blog.csdn.net/zhaoguanghui2012/article/details/52910035

一、CLR線程池

  管理線程開銷最好的方式:

  1. 盡量少的創建線程并且能將線程反復利用(線程池初始化時沒有線程,有程序請求線程則創建線程);
  2. 最好不要銷毀而是掛起線程達到避免性能損失(線程池創建的線程完成任務后以掛起狀態回到線程池中,等待下次請求);
  3. 通過一個技術達到讓應用程序一個個執行工作,類似于一個隊列(多個應用程序請求線程池,線程池會將各個應用程序排隊處理);
  4. 如果某一線程長時間掛起而不工作的話,需要徹底銷毀并且釋放資源(線程池自動監控長時間不工作的線程,自動銷毀);
  5. 如果線程不夠用的話能夠創建線程,并且用戶可以自己定制最大線程創建的數量(當隊列過長,線程池里的線程不夠用時,線程池不會坐視不理);

  微軟早就替我們想到了,為我們實現了線程池。

  CLR線程池并不會在CLR初始化時立即建立線程,而是在應用程序要創建線程來運行任務時,線程池才初始化一個線程。

  線程池初始化時是沒有線程的,線程池里的。線程的初始化與其他線程一樣,但是在完成任務以后,該線程不會自行銷毀,而是以掛起的狀態返回到線程池。直到應用程序再次向線程池發出請求時,線程池里掛起的線程就會再度激活執行任務。

  這樣既節省了建立線程所造成的性能損耗,也可以讓多個任務反復重用同一線程,從而在應用程序生存期內節約大量開銷。

通過CLR線程池所建立的線程總是默認為后臺線程,優先級數為ThreadPriority.Normal

二、工作者線程與I/O線程

  CLR線程池分為工作者線程(workerThreads)與I/O線程(completionPortThreads)兩種:

  • 工作者線程是主要用作管理CLR內部對象的運作,通常用于計算密集的任務。
  • I/O(Input/Output)線程主要用于與外部系統交互信息,如輸入輸出,CPU僅需在任務開始的時候,將任務的參數傳遞給設備,然后啟動硬件設備即可。等任務完成的時候,CPU收到一個通知,一般來說是一個硬件的中斷信號,此時CPU繼續后繼的處理工作。在處理過程中,CPU是不必完全參與處理過程的,如果正在運行的線程不交出CPU的控制權,那么線程也只能處于等待狀態,即使操作系統將當前的CPU調度給其他線程,此時線程所占用的空間還是被占用,而并沒有CPU處理這個線程,可能出現線程資源浪費的問題。如果這是一個網絡服務程序,每一個網絡連接都使用一個線程管理,可能出現大量線程都在等待網絡通信,隨著網絡連接的不斷增加,處于等待狀態的線程將會很消耗盡所有的內存資源。可以考慮使用線程池解決這個問題。

  線程池的最大值一般默認為1000、2000。當大于此數目的請求時,將保持排隊狀態,直到線程池里有線程可用。

  使用CLR線程池的工作者線程一般有兩種方式:

  • 通過ThreadPool.QueueUserWorkItem()方法;
  • 通過委托;

  要注意,不論是通過ThreadPool.QueueUserWorkItem()還是委托,調用的都是線程池里的線程。

三、ThreadPool類常用方法

  通過以下兩個方法可以讀取和設置CLR線程池中工作者線程與I/O線程的最大線程數。

  1. ThreadPool.GetMax(out in workerThreads,out int completionPortThreads);
  2. ThreadPool.SetMax(int workerThreads,int completionPortThreads);

  若想測試線程池中有多少線程正在投入使用,可以通過ThreadPool.GetAvailableThreads(out in workThreads,out int conoletionPortThreads)方法。

方法說明
GetAvailableThreads剩余空閑線程數
GetMaxThreads最多可用線程數,所有大于此數目的請求將保持排隊狀態,直到線程池線程變為可用
GetMinThreads檢索線程池在新請求預測中維護的空閑線程數。
QueueUserWorkItem啟動線程池里得一個線程(隊列的方式,如線程池暫時沒空閑線程,則進入隊列排隊)
SetMaxThreads設置線程池中的最大線程數
SetMinThreads設置線程池最少需要保留的線程數
復制代碼
    class Program{static void Main(string[] args){int i = 0;int j = 0;//前面是輔助(也就是所謂的工作者)線程,后面是I/O線程ThreadPool.GetMaxThreads(out i, out j);Console.WriteLine(i.ToString() + "   " + j.ToString()); //默認都是1000//獲取空閑線程,由于現在沒有使用異步線程,所以為空ThreadPool.GetAvailableThreads(out i, out j);Console.WriteLine(i.ToString() + "   " + j.ToString()); //默認都是1000
Console.ReadKey();}}
復制代碼

四、各種調用線程池線程的方法

  1、通過QueueUserWorkItem啟動工作者線程

  ThreadPool線程池中有兩個重載的靜態方法可以直接啟動工作者線程:

  •   ThreadPool.QueueUserWorkItem(waitCallback);
  •   ThreadPool.QueueUserWorkItem(waitCallback,Object);

  先把WaitCallback委托指向一個帶有Object參數的無返回值方法,再使用ThreadPool.QueueUserWorkItem(WaitCallback)就可以一步啟動此方法,此時異步方法的參數被視為null。

  下面來試下用QueueUserWorkItem啟動線程池里的一個線程。注意哦,由于是一直存在于線程池,所以不用new Thread()。

復制代碼
    class Program{static void Main(string[] args){//工作者線程最大數目,I/O線程的最大數目ThreadPool.SetMaxThreads(1000, 1000);   //啟動工作者線程ThreadPool.QueueUserWorkItem(new WaitCallback(RunWorkerThread));Console.ReadKey();}static void RunWorkerThread(object state){Console.WriteLine("RunWorkerThread開始工作");Console.WriteLine("工作者線程啟動成功!");}}
復制代碼

  輸出:

  

  使用第二個重載方法ThreadPool.QueueUserWorkItem(WaitCallback,object)方法可以把object對象作為參數傳送到回調函數中。

復制代碼
    class Program{static void Main(string[] args){Person p = new Person(1,"劉備");//啟動工作者線程ThreadPool.QueueUserWorkItem(new WaitCallback(RunWorkerThread), p);Console.ReadKey();}static void RunWorkerThread(object obj){Thread.Sleep(200);Console.WriteLine("線程池線程開始!");Person p = obj as Person;Console.WriteLine(p.Name);}}public class Person{public Person(int id,string name) { Id = id; Name = name; }public int Id { get; set; }public string Name { get; set; }}
復制代碼

  輸出結果如下:

  

  通過ThreadPool.QueueUserWork啟動工作者線程非常方便,但是WaitCallback委托指向的必須是一個帶有object參數的無返回值方法。所以這個方法啟動的工作者線程僅僅適合于帶單個參數和無返回值的情況。

  那么如果要傳遞多個參數和要有返回值又應該怎么辦呢?那就只有通過委托了。

  2、BeginInvoke與EndInvoke委托異步調用線程

  異步調用委托的步驟如下:

  1. 建立一個委托對象,通過IAsyncResult?BeginInvoke(string name,AsyncCallback callback,object state)異步調用委托方法,BeginInvoke方法除最后的兩個參數外,其他參數都是與方法參數相對應的。
  2. 利用EndInvoke(IAsyncResult--上一步BeginInvoke返回的對象)方法就可以結束異步操作,獲取委托的運行結果。
復制代碼
    class Program{//除了最后兩個參數,前面的都是你可定義的delegate string MyDelegate(string name,int age);static void Main(string[] args){//建立委托MyDelegate myDelegate = new MyDelegate(GetString);//異步調用委托,除最后兩個參數外,前面的參數都可以傳進去IAsyncResult result = myDelegate.BeginInvoke("劉備",22, null, null);  //IAsynResult還能輪詢判斷,功能不弱Console.WriteLine("主線程繼續工作!");//調用EndInvoke(IAsyncResult)獲取運行結果,一旦調用了EndInvoke,即使結果還沒來得及返回,主線程也阻塞等待了//注意獲取返回值的方式string data = myDelegate.EndInvoke(result);Console.WriteLine(data);Console.ReadKey();}static string GetString(string name, int age){Console.WriteLine("我是不是線程池線程" + Thread.CurrentThread.IsThreadPoolThread);Thread.Sleep(2000);return string.Format("我是{0},今年{1}歲!",name,age);}}
復制代碼

  輸出如下:

  

  這種方法有一個缺點,就是不知道異步操作什么時候執行完,什么時候開始調用EndInvoke,因為一旦EndInvoke主線程就會處于阻塞等待狀態。

  3、IAsyncResult輪詢

  為了克服上面提到的缺點,此時可以好好利用IAsyncResult提高主線程的工作性能,IAsyncResult有如下成員。

復制代碼
public interface IAsyncResult
{object AsyncState {get;}       //獲取用戶定義的對象,它限定或包含關于異步操作的信息。WailHandle AsyncWaitHandle {get;}  //獲取用于等待異步操作完成的 WaitHandle。bool CompletedSynchronously {get;} //獲取異步操作是否同步完成的指示。bool IsCompleted {get;}        //獲取異步操作是否已完成的指示。
}
復制代碼

  示例如下:

復制代碼
    class Program{delegate string MyDelegate(string name,int age);static void Main(string[] args){MyDelegate myDelegate = new MyDelegate(GetString);IAsyncResult result = myDelegate.BeginInvoke("劉備",22, null, null);Console.WriteLine("主線程繼續工作!");//比上個例子,只是利用多了一個IsCompleted屬性,來判斷異步線程是否完成 while (!result.IsCompleted){Thread.Sleep(500);          Console.WriteLine("異步線程還沒完成,主線程干其他事!");}string data = myDelegate.EndInvoke(result);Console.WriteLine(data);Console.ReadKey();}static string GetString(string name, int age){Thread.Sleep(2000);return string.Format("我是{0},今年{1}歲!",name,age);}}
復制代碼

  輸出如下:

  

  以上例子,除了IsCompleted屬性外,還可以使用AsyncWaitHandle如下3個方法實現同樣輪詢判斷效果:

  • WaitOne:判斷單個異步線程是否完成;
  • WaitAny:判斷是否異步線程是否有指定數量個已完成;
  • WaitAll:判斷是否所有的異步線程已完成;

  WaitOne:

  //比上個例子,判斷條件由IsCompleted屬性換成了AsyncWaitHandle,僅此而已while (!result.AsyncWaitHandle.WaitOne(200)){Console.WriteLine("異步線程沒完,主線程繼續干活!");}

  WaitAny:

  //是否完成了指定數量WaitHandle[] waitHandleList = new WaitHandle[] { result.AsyncWaitHandle };while (WaitHandle.WaitAny(waitHandleList, 200) > 0){Console.WriteLine("異步線程完成數未大于0,主線程繼續甘其他事!");}

  WaitAll:

  WaitHandle[] waitHandleList = new WaitHandle[] { result.AsyncWaitHandle };//是否全部異步線程完成while (!WaitHandle.WaitAll(waitHandleList, 200)){Console.WriteLine("異步線程未全部完成,主線程繼續干其他事!");}

  4、IAsyncResult回調函數

  使用輪詢方式來檢測異步方法的狀態非常麻煩,而且影響了主線程,效率不高。能不能異步線程完成了就直接調用實現定義好的處理函數呢?

  有,還是強大的IAsyncResult對象。

復制代碼
    class Program{delegate string MyDelegate(string name, int age);static void Main(string[] args){//建立委托MyDelegate myDelegate = new MyDelegate(GetString);//倒數第二個參數,委托中綁定了完成后的回調方法IAsyncResult result1 = myDelegate.BeginInvoke("劉備",23, new AsyncCallback(Completed), null);//主線程可以繼續工作而不需要等待Console.WriteLine("我是主線程,我干我的活,不再理你!");Thread.Sleep(5000);//Console.ReadKey();
        }static string GetString(string name, int age){Thread.CurrentThread.Name = "異步線程";//注意,如果不設置為前臺線程,則主線程完成后就直接卸載程序了//Thread.CurrentThread.IsBackground = false;Thread.Sleep(2000);return string.Format("我是{0},今年{1}歲!", name, age);}//供異步線程完成回調的方法static void Completed(IAsyncResult result){//獲取委托對象,調用EndInvoke方法獲取運行結果AsyncResult _result = (AsyncResult)result;MyDelegate myDelegaate = (MyDelegate)_result.AsyncDelegate;//獲得參數string data = myDelegaate.EndInvoke(_result);Console.WriteLine(data);//異步線程執行完畢Console.WriteLine("異步線程完成咯!");Console.WriteLine("回調函數也是由" + Thread.CurrentThread.Name + "調用的!");}}
復制代碼

  輸出如下:

  

  注意:

  1. 回調函數依然是在輔助線程中執行的,這樣就不會影響主線程的運行。
  2. 線程池的線程默認是后臺線程。但是如果主線程比輔助線程優先完成,那么程序已經卸載,回調函數未必會執行。如果不希望丟失回調函數中的操作,要么把異步線程設為前臺線程,要么確保主線程將比輔助線程遲完成。

  到目前為止,BeginInvoke("劉備",23, new AsyncCallback(Completed), null)還有最后一個參數沒用過的。那么最后一個參數是用來干什么?傳參:

復制代碼
namespace 控制臺___學習測試
{class Program{delegate string MyDelegate(string name, int age);static void Main(string[] args){Person p = new Person(2,"關羽");//建立委托MyDelegate myDelegate = new MyDelegate(GetString);//最后一個參數的作用,原來是用來傳參的IAsyncResult result1 = myDelegate.BeginInvoke("劉備", 23, new AsyncCallback(Completed), p);//主線程可以繼續工作而不需要等待Console.WriteLine("我是主線程,我干我的活,不再理你!");Console.ReadKey();}static string GetString(string name, int age){Thread.CurrentThread.Name = "異步線程";//注意,如果不設置為前臺線程,則主線程完成后就直接卸載程序了Thread.CurrentThread.IsBackground = false;Thread.Sleep(2000);return string.Format("我是{0},今年{1}歲!", name, age);}//供異步線程完成回調的方法static void Completed(IAsyncResult result){//獲取委托對象,調用EndInvoke方法獲取運行結果AsyncResult _result = (AsyncResult)result;MyDelegate myDelegaate = (MyDelegate)_result.AsyncDelegate;//獲得參數string data = myDelegaate.EndInvoke(_result);Console.WriteLine(data);Person p = result.AsyncState as Person;Console.WriteLine("傳過來的參數是:" + p.Name);//異步線程執行完畢Console.WriteLine("異步線程完成咯!");Console.WriteLine("回調函數也是由" + Thread.CurrentThread.Name + "調用的!");}}public class Person{public Person(int id, string name){Id = id;Name = name;}public int Id{get;set;}public string Name{get;set;}}
}
復制代碼

  輸出如下:

  


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/458460.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/458460.shtml
英文地址,請注明出處:http://en.pswp.cn/news/458460.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

蘋果應用上架,圖片的要求(2017年4月27日)

看這個提示應該就明白了吧。 哈哈&#xff0c;我還是自己再說一遍加深一下印象吧&#xff1a;如果應用在各個尺寸iphone屏幕上面外觀一樣&#xff0c;就只準備5.5英寸的圖就可以了&#xff1b;如果有所不同&#xff0c;就按照實際情況&#xff0c;準備不同屏幕尺寸的圖片即可。…

jQuery操作checkbox

2012歐洲杯"死亡之組"小組出線的國家隊是&#xff1a;<br> <inputtype"checkbox"name"nation"value"Germany">德國 <inputtype"checkbox"name"nation"value"Denmark">丹麥 <input…

android Instrumentation 轉載

Android提供了一系列強大的測試工具&#xff0c;它針對Android的環境&#xff0c;擴展了業內標準的JUnit測試框架。盡管你可以使用JUnit測試Android工程&#xff0c;但Android工具允許你為應用程序的各個方面進行更為復雜的測試&#xff0c;包括單元層面及框架層面。Android測試…

Linker command failed with exit code 1(use -v to see invocation)

Linker command failed with exit code 1(use -v to see invocation) 出現這個問題的原因是&#xff1a;工程當中存在相同的文件&#xff0c;找到該文件將其刪除即可

【C#學習筆記】使用C#中的Dispatcher

form:https://www.jianshu.com/p/0714fc755988之前的文章說過了如何使用BackgroundWorker&#xff0c;今天要說的是WPF程序員處理多線程的另外一個方式 - Dispatcher當我們打開一個WPF應用程序即開啟了一個進程&#xff0c;該進程中至少包含兩個線程。一個線程用于處理呈現&…

流媒體 關鍵詞解釋

流媒體 流媒體是指采用流式傳輸的方式在網上播放的媒體格式, 是邊傳邊播的媒體&#xff0c;是多媒體的一種! 然后就是大家需要了解的幾個關鍵詞 幀:視頻是由很多連續圖像組成, 每一幀就代表一幅靜止的圖像 GOP:&#xff08;Group of Pictures&#xff09;畫面組&#xff0c;一個…

[C#] 等待啟動的進程執行完畢

from: https://www.cnblogs.com/qqhfeng/p/4769524.html有能有時候我們啟動了一個進程&#xff0c;必須等到此進程執行完畢&#xff0c;或是&#xff0c;一段時間&#xff0c; 關閉進程后再繼續往下走。Example sample1 等待應用程序執行完畢 //等待應用程序執行完畢private vo…

html body標簽

table table 屬性&#xff1a; border &#xff1a;定義表格的邊框寬度&#xff0c;默認為0&#xff0c;即無邊框。<table border"1"> title &#xff1a;表格的提示信息&#xff0c;當鼠標移到表格上方時&#xff0c;所提示的信息。 cellpadding &#xff1a;…

創建字符串的方法

//創建一個字符串常量 NSString *str"字符串"; //創建一個空的字符對象 NSString *str1[[NSString alloc]init];//實例方法 NSString *str2[NSString string];//類方法 //快速創建一個字符串 NSString *str3[[NSString alloc]initWithString:"字符串"];//實…

DataReceivedEventHandler 委托 接收調用執行進程返回數據

https://msdn.microsoft.com/zh-cn/library/azure/system.diagnostics.datareceivedeventhandler備注創建 DataReceivedEventHandler 委托時&#xff0c;需要標識將處理該事件的方法。 若要將事件與事件處理程序關聯&#xff0c;請將該委托的一個實例添加到事件中。 除非移除了…

coreData

CoreData使用 創建步驟流程 第一步先創建.xcdatamodeld文件&#xff08;New File -> iOS -> Core Data ->Data Model&#xff09; 屏幕快照 2016-07-07 下午10.40.16.png名字雖然可以任意取&#xff0c;但最好還是取和自己存儲數據庫名字一樣的名字。這樣可讀性更高些…

命令行下mysql新建用戶及分配權限

創建用戶&#xff1a; CREATE USER usernamehost IDENTIFIED BY password; 說明:username – 你將創建的用戶名, host – 指定該用戶在哪個主機上可以登陸,如果是本地用戶可用localhost, 如 果想讓該用戶可以從任意遠程主機登陸,可以使用通配符%. password – 該用戶的登陸密…

PetaPoco初體驗(轉)

PetaPoco初體驗&#xff08;轉&#xff09; PetaPoco初體驗&#xff08;轉&#xff09;大部分轉自&#xff1a; http://landyer.com/archives/138 PetaPoco C#微型ORM框架&#xff0c;基本無需配置&#xff0c;僅由單個cs文件構成&#xff0c;支持.net3.5 .net4.0。 截稿時Peta…

iOS當中的設計模式

代理模式 應用場景&#xff1a;當一個類的某些功能需要由別的類來實現&#xff0c;但是又不確定具體會是哪個類實現。 優勢&#xff1a;解耦合 敏捷原則&#xff1a;開放-封閉原則 實例&#xff1a;tableview的 數據源delegate&#xff0c;通過和protocol的配合&#xff0c…

c#的dllimport使用方法詳解

DllImport是System.Runtime.InteropServices命名空間下的一個屬性類&#xff0c;其功能是提供從非托管DLL&#xff08;托管/非托管是微軟的.net framework中特有的概念&#xff0c;其中&#xff0c;非托管代碼也叫本地&#xff08;native&#xff09;代碼。與Java中的機制類似&…

each函數循環數據表示列舉,列舉循環的時候添加dom的方法

var dotBox $(#bannerNum);var item <li></li>;var itemSize $(#bannerBack p).length;var dotBoxWidth itemSize*24;$(#bannerBack p).each(function () { dotBox.append(<li></li>); dotBox.find(li).eq(0).addClass(current);});這里要根…

使用lxml代替beautifulsoup

深入使用過lxml的都會深深地喜歡上它,雖然BeautifulSoup很流行,但是深入了解lxml后,你就再也不會使用bs了 我覺得beautifulsoup不好用,速度也慢(雖然可以使用lxml作為解析器了).另外soup.find_all這種簡直就是手工時代的操作(很多人使用find find_all這幾個函數, 其實它們使用起…

QT調用dll且進入DLL src code調試

qt應用程序AA.exe調用qt寫的BB.DLL時&#xff0c;進入BB.DLL src code調試 1、debug生成AA.exe 2、將debug生成的AA.exe整包debug folder放到BB.dll的debug生成的文件夾中 3、設置BB.dll這個project&#xff1a;項目----運行-----Executable中選擇BB.dll的debug文件夾中的AA.ex…

php安裝編譯時 configure: error: Cannot find OpenSSL's evp.h

yum install error: protected multilib versions errorsudo yum downgrade openssl 降級sudo yum install openssl-devel 另外參考yum install -y ncurses-devel yum install openssl openssl-develyum install openssl openssl-devel --setoptprotected_multilibfalse ln -s …

laravel項目中css樣式表的背景圖片不顯示

剛學laravel&#xff0c;遇到了很多坑&#xff0c;感覺laravel是挺強大的。 建好后臺項目&#xff0c;奈何css樣式表的背景圖片不顯示 .mainhd {background: url(../images/sky/body_bg.png) repeat-x 0px 0px; } 按理上面的寫法沒錯&#xff0c;因為是從別的后臺搬過來的&…