SmartThreadPool Github地址:
https://github.com/amibar/SmartThreadPool
首先是實例化的時候的參數的解釋
//Initialize SmartThreadPool & Make logs //SmartThreadPool m_hThreadPool; //m_hThreadPool = new SmartThreadPool();//聲明一個線程池 STPStartInfo stp = new STPStartInfo();//線程詳細配置參數 //m_hThreadPool.STPStartInfo這個屬性是只讀屬性,所以只能在實例化的時候設置 { stp.AsReadOnly();//返回一個只讀類型的STPStartInfo //一個枚舉值,儲存工作項執行完成后是否調用回調方法, //Never不調用, //WhenWorkItemCanceled只有當工作項目被取消時調用 //WhenWorkItemNotCanceled只有當工作項目不取消調用 //Always調用 stp.CallToPostExecute = CallToPostExecute.Always;//在這里選擇總是回調 //當工作項執行完成后,是否釋放工作項的參數,如果釋放,參數對象必須實現IDisposable接口 stp.DisposeOfStateObjects = true; //當線程池中沒有工作項時,閑置的線程等待時間,超過這個時間后,會釋放掉這個閑置的線程,默認為60秒 stp.IdleTimeout = 300;//300s //最大線程數,默認為25, //注意,由於windows的機制,所以一般最大線程最大設置成25, //如果設置成0的話,那么線程池將停止運行 stp.MaxWorkerThreads = 15;//15 thread //只在STP執行Action<...>與Func<...>兩種任務時有效 //在執行工作項的過程中,是否把參數傳遞到WorkItem中去,用做IWorkItemResult接口取State時使用, //如果設置為false那么IWorkItemResult.State是取不到值的 //如果設置為true可以取到傳入參數的數組 stp.FillStateWithArgs = true; //最小線程數,默認為0,當沒有工作項時,線程池最多剩余的線程數 stp.MinWorkerThreads = 5;//5 thread //當工作項執行完畢后,默認的回調方法 stp.PostExecuteWorkItemCallback = delegate(IWorkItemResult wir) { MessageBox.Show("ok" + wir.Result); }; //是否需要等待start方法后再執行工作項,?默認為true,當true狀態時,STP必須執行Start方法,才會為線程分配工作項 stp.StartSuspended = true; } m_hThreadPool = new SmartThreadPool(stp);//帶線程初始化的線程池初始化
以下是使用兩種方法定義函數運行等待返回結果的演示,一種是等待實例化中的對象全部執行完成,一種是等待其中的某些執行完成。
private void button1_Click(object sender, EventArgs e) { //這個例子將演示傳入數個參數並且等待運行然后傳出的全過程 SmartThreadPool stp = new SmartThreadPool(); IWorkItemResult<string> resultCallback = stp.QueueWorkItem(new Amib.Threading.Func<string, string, string>(GetResultstring), "hello ", "world"); stp.Start(); stp.WaitForIdle();//等待該實例下的所有結果返回 MessageBox.Show(resultCallback.Result); stp.Shutdown(); } private string GetResultstring(string str, string str2) { return str + str2; }
private void button2_Click(object sender, EventArgs e) { //這個例子將演示一批參數的傳入一批線程並且等待執行結束返回值 SmartThreadPool stp = new SmartThreadPool(); List<IWorkItemResult> t_lResultItem = new List<IWorkItemResult>();//不對IWorkItemResult定義其類型,其結果需要自己做類型轉換 for (int step = 0; step != 100;step++ ) { //這里使用另一種方法來做函數 t_lResultItem.Add(stp.QueueWorkItem(new WorkItemCallback(GetObjectString), new string[] { "hello ", step.ToString() })); } stp.Start(); //等待所需的結果返回 if (SmartThreadPool.WaitAll(t_lResultItem.ToArray())) { foreach (IWorkItemResult t in t_lResultItem) { MakeLog(string.Format("{0}{1}", t.State, t.Result)); } } } private object GetObjectString(object obj) { return string.Format("{0}{1}", (obj as string[])[0], (obj as string[])[1]); }
處理線程執行過程中出現的錯誤
private void button3_Click(object sender, EventArgs es) { //處理線程執行過程中出現的錯誤 SmartThreadPool stp = new SmartThreadPool();//如果需要將線程池設置為調用start的時候才運行,需要設置其StartSuspended參數為true,然后為其調用start方法來啟動 IWorkItemResult<double> ret = stp.QueueWorkItem(new Amib.Threading.Func<double, double, double>(Diverse), 10.0, 0); //接收錯誤的句柄 stp.Start(); Exception e = null; double resule = ret.GetResult(out e);//在取出結果的時候判斷是否有錯誤產生 if (e != null) { //在這里進行錯誤處理,錯誤在InnerException中 MessageBox.Show(e.InnerException.Message); } else { MessageBox.Show(resule.ToString()); } stp.Shutdown(); } private double Diverse(double x, double y) { return x/y; }
使用線程分組
private void button4_Click(object sender, EventArgs e) { //這里演示了線程的分組 SmartThreadPool stp = new SmartThreadPool(); //創建一個分組並用這個分組管理 IWorkItemsGroup mainGroup = stp.CreateWorkItemsGroup(1);//如果需要設置這個分組為調用start的時候才開始運行,需要傳入WIGStartInfo參數,將其參數中的StartSuspended設置為true然后調用分組的start方法 //向分組中添加任務->當然可以有返回值 mainGroup.QueueWorkItem(new WorkItemCallback(GetObjectString), 123); //分組等待所有任務完成 mainGroup.WaitForIdle(); //關閉 stp.Shutdown(); }
以下是SmartThreadPool的部分解釋
SmartThreadPool smartThreadPool = new SmartThreadPool(); //獲取當前線程池中的工作線程數,與InUseThreads可能會有差別,因為InUseThreads不包含Idle狀態的線程 int threadNum = smartThreadPool.ActiveThreads; //取消所有工作項,如果工作項在執行,那么等待工作項執行完 smartThreadPool.Cancel(); //如果不想等待工作項執行完, smartThreadPool.Cancel(true); //線程池的最大並發數,即MaxWorkerThreads, //如果修改后的Concurrency小於MinWorkerThreads,那么MinWorkerThreads也會隨之改變 smartThreadPool.Concurrency = 25; //創建一個工作組,最大並發為3,工作組在后面會詳細說明, smartThreadPool.CreateWorkItemsGroup(3); //卸載線程池 smartThreadPool.Dispose(); //反回所有未執行的工作項的參數對象 smartThreadPool.GetStates(); //獲取線程池中正在工作的線程數,與ActiveThreads會有差別,因為ActiveThreads可能包含Idle狀態的線程 int useThreadNum = smartThreadPool.InUseThreads; //當線程池用沒有工作項時,反回true,否則,反回false bool IsIdle = smartThreadPool.IsIdle; //同時並行執行多個方法,並且阻塞到所有工作項都執行完,這里會有多少個工作項就會創造多少個線程, smartThreadPool.Join(new Action[] { new Action(Test) }); //獲取或設置最大線程數,即MaxWorkerThreads, smartThreadPool.MaxThreads = 25; //最小線程數,當沒有工作項時,線程池最多剩余的線程數 smartThreadPool.MinThreads = 0; //線程池的名稱,沒什么特殊的用處, smartThreadPool.Name = "StartThreadPool"; //當線程池中沒有工作項(即閑置)時觸發的事件 smartThreadPool.OnIdle += new WorkItemsGroupIdleHandler(smartThreadPool_OnIdle); //當線程池啟動一個線程時,觸發的事件 smartThreadPool.OnThreadInitialization += new ThreadInitializationHandler(smartThreadPool_OnThreadInitialization); //當線程池釋放一個線程時,所觸發的事件 smartThreadPool.OnThreadTermination += new ThreadTerminationHandler(smartThreadPool_OnThreadTermination); //與Join方法類似,並行執行多個帶參數的方法,這里會有多少個工作項就會創造多少個線程 smartThreadPool.Pipe<object>(new object(), new Action<object>[] { new Action<object>(Test) }); //卸載線程池 smartThreadPool.Shutdown(); //啟動線程池 smartThreadPool.Start(); //STPStartInfo對象的只讀實例 STPStartInfo stpStartInfo = smartThreadPool.STPStartInfo; //等待所有的工作項執行完成(即IsIdle為true) smartThreadPool.WaitForIdle(); //獲取還未執行的工作項數量 int wiNum = smartThreadPool.WaitingCallbacks; //WorkItemGroup的啟動信息的只讀實力 WIGStartInfo wigStartInfo = smartThreadPool.WIGStartInfo;
****************
1、為什么需要使用線程池(Thread Pool)
- 減少線程間上下文切換。線程執行一定的時間片后,系統會自動把cpu切換給另一個線程使用,這時還需要保存當 前的線程上下文狀態,並加載新線程的上下文狀態。當程序中有大量的線程時,每個線程分得的時間片會越來越少,可能會出現線程未處理多少操作,就需要切換到 另一線程,這樣頻繁的線程間上下文切換會花費大量的cpu時間。
- 減少內存占用。系統每創建一條物理線程,需要大概花費1MB的內存空間,許多程序喜歡先創建多條物理線程,並 周期輪詢來處理各自的任務,這樣既消耗了線程上下文切換的時間,還浪費了內存。這些任務可能只需要一條線程就能滿足要求。假如某一任務需要執行較長的周 期,線程池還可以自動增加線程,並在空閑時,銷毀線程,釋放占用的內存。
2、為什么不使用.Net默認的線程池
- .Net默認的線程池(ThreadPool)是一個靜態類,所以是沒辦法自己創建一個新的程序池的。默認的線程池與應用程序域 (AppDomain)掛鈎,一個AppDomain只有一個線程池。假如在線程池中執行了一個周期較長的任務,一直占用着其中一個線程,可能就會影響到 應用程序域中的其他程序的性能。例如,假如在Asp.Net的線程池中執行一個周期較長的任務,就會影響請求的並發處理能力(線程池默認有個最大線程 數)。 3、SmartThreadPool特性和優點
SmartThreadPool特性如下:
- 池中的線程數量會根據負載自動增減
- 任務異步執行后可以返回值
- 處於任務隊列中未執行的任務可以取消
- 回調函數可以等待多個任務都執行完成后再觸發
- 任務可以有優先級(priority)
- 任務可以分組
- 支持泛型Action<T> 和 Func<T>
- 有性能監測機制
4、使用示例 最簡單的使用方法:
1
2
3
4
5
6
7
8
|
// 創建一個線程池
SmartThreadPool smartThreadPool =
new
SmartThreadPool();
// 執行任務
smartThreadPool.QueueWorkItem(() =>
{
Console.WriteLine(
"Hello World!"
);
});
|
帶返回值的任務:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// 創建一個線程池
SmartThreadPool smartThreadPool =
new
SmartThreadPool();
// 執行任務
var result = smartThreadPool.QueueWorkItem(() =>
{
var sum = 0;
for
(var i = 0; i < 10; i++)
sum += i;
return
sum;
});
// 輸出計算結果
Console.WriteLine(result.Result);
|
等待多個任務執行完成:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
// 創建一個線程池
SmartThreadPool smartThreadPool =
new
SmartThreadPool();
// 執行任務
var result1 = smartThreadPool.QueueWorkItem(() =>
{
//模擬計算較長時間
Thread.Sleep(5000);
return
3;
});
var result2 = smartThreadPool.QueueWorkItem(() =>
{
//模擬計算較長時間
Thread.Sleep(3000);
return
5;
});
bool
success = SmartThreadPool.WaitAll(
new
IWorkItemResult[] { result1, result2 });
if
(success)
{
// 輸出結果
Console.WriteLine(result1.Result);
Console.WriteLine(result2.Result);
}
|
5、結論 使用SmartThreadPool可以簡單就實現支持多線程的程序,由線程池來管理線程,可以減少死鎖的出現。SmartThreadPool還支持簡單的生產者-消費者模式,當不需要對任務進行持久化時,還是很好用的。
6、擴展閱讀 http://www.codeproject.com/KB/threads/smartthreadpool.aspx