現行遇到一個狀況,需要使用多執行緒連線TCP Server。
原先我是想使用Thread,不過在查詢如何使用多個執行緒設定時,發現了另外一個可用的方法ThreadPool
首先是Microsoft目前並不建議直接使用Thread控制,加上ThreadPool可以直接在for迴圈內使用,自動增加多個執行緒。對於我目前要做一個壓測用的程式而言,只要在設定檔寫好測試次數或者測試時間,用for迴圈執行ThreadPool就能得到想要的結果。實在是太方便了!
不過,在這當中我遇到了一些問題,為防止自己忘記,先記錄在此。
- 依照說明傳入是非固定的object,該如何設為固定的物件?
- 設定的執行緒的最大值,但實際程式執行時,同一時間內的執行緒超過上限...
首先是關於第一項的問題,該如何將object轉為固定的物件?
關於這一點,必須先說明ThreadPool如何使用...
下面的範例是我用來測試使用的,不過後來改過,所以不確定這個是否可以執行...
static void Main(string[] args) { for(var i=0;i<100;i++) { ThreadPool.QueueUserWorkItem(new WaitCallback(countIndex), i); } } private static void countIndex(object? obj) { string timeStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"); string msg = $"{timeStr}\t[{obj}] \t[{Thread.CurrentThread.ManagedThreadId}] \t This is Test!!"; Console.WriteLine(msg); }
測試沒有問題後,我發現我要帶入的是一個完整的資料格式。
我用過不少方法,都沒有成功,最後改為下面的方式就可以執行了。
不過必須要用try catch包好,物件轉換錯誤可能會讓程式整個當掉,但因為是內部使用,就沒寫那麼多防呆了。
static void Main(string[] args) { for(var i=0;i<100;i++) { DTO dto = new dto; dto.Index = i; ... ThreadPool.QueueUserWorkItem(new WaitCallback(countIndex), dto); } } private static void countIndex(object? obj) { try { //這裡的轉換物件須注意 DTO dto = (DTO)obj; ... string msg = getMessage(obj.Index, dto.Message); Console.WriteLine(msg); } catch (Exception ex) { string msg = getMessage(0, $"error = {ex.ToString()}"); Console.WriteLine(msg); } } private static string getMessage(int Index, string msg) { string timeStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"); return $"{timeStr}\t[{Index}] \t[{Thread.CurrentThread.ManagedThreadId}] \t {msg}"; }
其次是關於執行緒上限的設定...
依照我上方的程式碼,執行緒ID與Index是可以做比對的,所以我發現了在同一時間,執行緒超出我設定的上限...
當時的程式碼大致如下:
static void Main(string[] args) { ThreadPool.SetMaxThreads(5, 5); for(var i=0;i<100;i++) { DTO dto = new dto; dto.Index = i; ... ThreadPool.QueueUserWorkItem(new WaitCallback(countIndex), dto); } } private static void countIndex(object? obj) { try { //這裡的轉換物件須注意 DTO dto = (DTO)obj; ... string msg = getMessage(obj.Index, dto.Message); Console.WriteLine(msg); } catch (Exception ex) { string msg = getMessage(0, $"error = {ex.ToString()}"); Console.WriteLine(msg); } } private static string getMessage(int Index, string msg) { string timeStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"); return $"{timeStr}\t[{Index}] \t[{Thread.CurrentThread.ManagedThreadId}] \t {msg}"; }
但是實際執行,跳出的執行緒至少超過10個...
這和我當初想像的結果完全不同,測試過許多方法都沒有用,最後才發現只設定SetMaxThreads是不行的!
將程式碼調整成下方後,測試的結果終於與我想的相同了。Thread.CurrentThread.ManagedThreadId出現的號碼只有設定的數量,同一個時間執行緒也不會超過上限...
static void Main(string[] args) { //min & max 必須同時設定才有效... ThreadPool.SetMinThreads(2, 2); ThreadPool.SetMaxThreads(5, 5); for(var i=0;i<100;i++) { DTO dto = new dto; dto.Index = i; ... ThreadPool.QueueUserWorkItem(new WaitCallback(countIndex), dto); } } private static void countIndex(object? obj) { try { //這裡的轉換物件須注意 DTO dto = (DTO)obj; ... string msg = getMessage(obj.Index, dto.Message); Console.WriteLine(msg); } catch (Exception ex) { string msg = getMessage(0, $"error = {ex.ToString()}"); Console.WriteLine(msg); } } private static string getMessage(int Index, string msg) { string timeStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"); return $"{timeStr}\t[{Index}] \t[{Thread.CurrentThread.ManagedThreadId}] \t {msg}"; }
參考網址:
Microsoft官網說明
kinanson的技術回憶
安德魯的部落格
余小章 @ 大內殿堂
玩轉C#之【執行序-實際實作】
C# .NET Blazor MAUI Xamarin Research
沒有留言:
張貼留言