ShowProgramCode

2022年12月15日 星期四

C# .Net Core6 多執行緒處理ThreadPool

現行遇到一個狀況,需要使用多執行緒連線TCP Server。
原先我是想使用Thread,不過在查詢如何使用多個執行緒設定時,發現了另外一個可用的方法ThreadPool

首先是Microsoft目前並不建議直接使用Thread控制,加上ThreadPool可以直接在for迴圈內使用,自動增加多個執行緒。對於我目前要做一個壓測用的程式而言,只要在設定檔寫好測試次數或者測試時間,用for迴圈執行ThreadPool就能得到想要的結果。實在是太方便了!

不過,在這當中我遇到了一些問題,為防止自己忘記,先記錄在此。

  1.  依照說明傳入是非固定的object,該如何設為固定的物件?
  2.  設定的執行緒的最大值,但實際程式執行時,同一時間內的執行緒超過上限...

首先是關於第一項的問題,該如何將object轉為固定的物件?
關於這一點,必須先說明ThreadPool如何使用...
下面的範例是我用來測試使用的,不過後來改過,所以不確定這個是否可以執行...

  1. static void Main(string[] args)
  2. {
  3. for(var i=0;i<100;i++)
  4. {
  5. ThreadPool.QueueUserWorkItem(new WaitCallback(countIndex), i);
  6. }
  7. }
  8.  
  9. private static void countIndex(object? obj)
  10. {
  11. string timeStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
  12. string msg = $"{timeStr}\t[{obj}] \t[{Thread.CurrentThread.ManagedThreadId}] \t This is Test!!";
  13. Console.WriteLine(msg);
  14. }

測試沒有問題後,我發現我要帶入的是一個完整的資料格式。
我用過不少方法,都沒有成功,最後改為下面的方式就可以執行了。
不過必須要用try catch包好,物件轉換錯誤可能會讓程式整個當掉,但因為是內部使用,就沒寫那麼多防呆了。

  1. static void Main(string[] args)
  2. {
  3. for(var i=0;i<100;i++)
  4. {
  5. DTO dto = new dto;
  6. dto.Index = i;
  7. ...
  8. ThreadPool.QueueUserWorkItem(new WaitCallback(countIndex), dto);
  9. }
  10. }
  11.  
  12. private static void countIndex(object? obj)
  13. {
  14. try
  15. {
  16. //這裡的轉換物件須注意
  17. DTO dto = (DTO)obj;
  18. ...
  19. string msg = getMessage(obj.Index, dto.Message);
  20. Console.WriteLine(msg);
  21. }
  22. catch (Exception ex)
  23. {
  24. string msg = getMessage(0, $"error = {ex.ToString()}");
  25. Console.WriteLine(msg);
  26. }
  27. }
  28.  
  29. private static string getMessage(int Index, string msg)
  30. {
  31. string timeStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
  32. return $"{timeStr}\t[{Index}] \t[{Thread.CurrentThread.ManagedThreadId}] \t {msg}";
  33. }

其次是關於執行緒上限的設定...
依照我上方的程式碼,執行緒ID與Index是可以做比對的,所以我發現了在同一時間,執行緒超出我設定的上限...
當時的程式碼大致如下:

  1. static void Main(string[] args)
  2. {
  3. ThreadPool.SetMaxThreads(5, 5);
  4. for(var i=0;i<100;i++)
  5. {
  6. DTO dto = new dto;
  7. dto.Index = i;
  8. ...
  9. ThreadPool.QueueUserWorkItem(new WaitCallback(countIndex), dto);
  10. }
  11. }
  12.  
  13. private static void countIndex(object? obj)
  14. {
  15. try
  16. {
  17. //這裡的轉換物件須注意
  18. DTO dto = (DTO)obj;
  19. ...
  20. string msg = getMessage(obj.Index, dto.Message);
  21. Console.WriteLine(msg);
  22. }
  23. catch (Exception ex)
  24. {
  25. string msg = getMessage(0, $"error = {ex.ToString()}");
  26. Console.WriteLine(msg);
  27. }
  28. }
  29.  
  30. private static string getMessage(int Index, string msg)
  31. {
  32. string timeStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
  33. return $"{timeStr}\t[{Index}] \t[{Thread.CurrentThread.ManagedThreadId}] \t {msg}";
  34. }

但是實際執行,跳出的執行緒至少超過10個...
這和我當初想像的結果完全不同,測試過許多方法都沒有用,最後才發現只設定SetMaxThreads是不行的!
將程式碼調整成下方後,測試的結果終於與我想的相同了。Thread.CurrentThread.ManagedThreadId出現的號碼只有設定的數量,同一個時間執行緒也不會超過上限...

  1. static void Main(string[] args)
  2. {
  3. //min & max 必須同時設定才有效...
  4. ThreadPool.SetMinThreads(2, 2);
  5. ThreadPool.SetMaxThreads(5, 5);
  6. for(var i=0;i<100;i++)
  7. {
  8. DTO dto = new dto;
  9. dto.Index = i;
  10. ...
  11. ThreadPool.QueueUserWorkItem(new WaitCallback(countIndex), dto);
  12. }
  13. }
  14.  
  15. private static void countIndex(object? obj)
  16. {
  17. try
  18. {
  19. //這裡的轉換物件須注意
  20. DTO dto = (DTO)obj;
  21. ...
  22. string msg = getMessage(obj.Index, dto.Message);
  23. Console.WriteLine(msg);
  24. }
  25. catch (Exception ex)
  26. {
  27. string msg = getMessage(0, $"error = {ex.ToString()}");
  28. Console.WriteLine(msg);
  29. }
  30. }
  31.  
  32. private static string getMessage(int Index, string msg)
  33. {
  34. string timeStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
  35. return $"{timeStr}\t[{Index}] \t[{Thread.CurrentThread.ManagedThreadId}] \t {msg}";
  36. }

參考網址:
Microsoft官網說明
kinanson的技術回憶
安德魯的部落格
余小章 @ 大內殿堂
玩轉C#之【執行序-實際實作】
C# .NET Blazor MAUI Xamarin Research

沒有留言:

張貼留言