ShowProgramCode

2022年12月30日 星期五

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

從上一篇可知ThreadPool的基本設定了,不過接下來又遇上新的問題。

  1.  如何確認所有執行緒完成?
  2.  將主控台專案改為WinForm專案使用ThreadPool...

首先,先說明如何確認所有執行緒完成工作?
在此我使用者WaitHandle.WaitAll()來確認。
依照官網的說明,必須將doneEvent = new ManualResetEvent(false)帶入ThreadPool,最終使用doneEvent陣列帶入WaitHandle.WaitAll()即可。
二話不說,先上程式碼。

  1. internal class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. const int FibonacciCalculations = 10;
  6. firtstTest(FibonacciCalculations);
  7. }
  8. private static void ThreadPoolCallbackV1(object obj)
  9. {
  10. if(obj == null) return;
  11. ManualResetEvent doneEvent = (ManualResetEvent)obj;
  12. Console.WriteLine(nameof(ThreadPoolCallbackV1));
  13. doneEvent.Set();
  14. }
  15. private static void firtstTest(int theadCount)
  16. {
  17. ThreadPool.SetMinThreads(2, 2);
  18. ThreadPool.SetMaxThreads(3, 3);
  19. ManualResetEvent[] doneEvents = new ManualResetEvent[theadCount];
  20. for(var i = 0; i < theadCount; i++)
  21. {
  22. doneEvents[i] = new ManualResetEvent(false);
  23. Console.WriteLine($"{nameof(firtstTest)} 第 {i} 次執行...");
  24. ThreadPool.QueueUserWorkItem(ThreadPoolCallbackV1, doneEvents[i]);
  25. }
  26. WaitHandle.WaitAll(doneEvents);
  27. Console.WriteLine("All calculations are complete.");
  28. }
  29. }

是的,這個是主控台的程式碼,當所有執行緒完成後,才會印出最後一個句子。

接下來的問題,就是要把這段程式碼改道WomForm的專案了...
最剛開始,我想...在主控台都測通過了,沒有問題。於是,隨便開了個空的Form表單,就把程式碼貼過去了。
結果? 當然是失敗了...

我花了至少三天才搞定它,雖然解法非常簡單...
二話不說,先上程式碼。

Program.cs

  1. internal static class Program
  2. {
  3. ///
  4. /// The main entry point for the application.
  5. ///
  6. //[STAThread] 將STAThread改為MTAThread,WaitHandle.WaitAll不支援STAThread,改為MTAThread就能夠執行...
  7. [MTAThread]
  8. static void Main()
  9. {
  10. // To customize application configuration such as set high DPI settings or default font,
  11. // see https://aka.ms/applicationconfiguration.
  12. ApplicationConfiguration.Initialize();
  13. Application.Run(new Form1());
  14. }
  15. }

Form1.cs

  1. private string _content = string.Empth;
  2. public partial class Form1 : Form
  3. {
  4. public TpcGrpcStressTest()
  5. {
  6. InitializeComponent();
  7. }
  8. private static void ThreadPoolCallbackV1(object obj)
  9. {
  10. if(obj == null) return;
  11. ManualResetEvent doneEvent = (ManualResetEvent)obj;
  12. _content += nameof(ThreadPoolCallbackV1) + Environment.NewLine;
  13. doneEvent.Set();
  14. }
  15.  
  16. private static void firtstTest(int theadCount)
  17. {
  18. ThreadPool.SetMinThreads(2, 2);
  19. ThreadPool.SetMaxThreads(3, 3);
  20. ManualResetEvent[] doneEvents = new ManualResetEvent[theadCount];
  21. for(var i = 0; i < theadCount; i++)
  22. {
  23. doneEvents[i] = new ManualResetEvent(false);
  24. _content += $"{nameof(firtstTest)} 第 {i} 次執行..." + Environment.NewLine;
  25. ThreadPool.QueueUserWorkItem(ThreadPoolCallbackV1, doneEvents[i]);
  26. }
  27. WaitHandle.WaitAll(doneEvents);
  28. }
  29.  
  30. private void SubmitBtn_Click(object sender, EventArgs e)
  31. {
  32. firtstTest(10);
  33. TextBox1.Text = _content + "All calculations are complete.";
  34. }
  35. }

答案超簡單,但困擾我許久,特別記錄下來,免得下次又發生。

參考網址: AutoResetEvent.WaitAll 等到人生三大事,然后大笑开心。


本以為到此為止,但是當我測試超過100筆執行緒時,又一個問題出現了...
System.NotSupportedException: 'The number of WaitHandles must be less than or equal to 64.'

再去查了資訊,才發現原來超過64個執行緒使用WaitHandle.WaitAll會錯誤...
實際找到解法後,說明不需要使用WaitHandle.WaitAll來檢查程序是否執行完成,那麼二話不說,修改程式碼...

Program.cs

  1. internal static class Program
  2. {
  3. ///
  4. /// The main entry point for the application.
  5. ///
  6. [STAThread]
  7. static void Main()
  8. {
  9. // To customize application configuration such as set high DPI settings or default font,
  10. // see https://aka.ms/applicationconfiguration.
  11. ApplicationConfiguration.Initialize();
  12. Application.Run(new Form1());
  13. }
  14. }

Form1.cs

  1. public partial class Form1 : Form
  2. {
  3. private string _content = string.Empth;
  4. private static int _numerOfThreadsNotYetCompleted = 0;
  5. private static ManualResetEvent _doneEvent = new ManualResetEvent(false);
  6. public TpcGrpcStressTest()
  7. {
  8. InitializeComponent();
  9. }
  10. private static void ThreadPoolCallbackV1(object obj)
  11. {
  12. if(obj == null) return;
  13. try
  14. {
  15. ManualResetEvent doneEvent = (ManualResetEvent)obj;
  16. _content += $"{nameof(ThreadPoolCallbackV1)} 第 {(int)i} 次執行...") + Environment.NewLine;
  17. doneEvent.Set();
  18. }
  19. finally
  20. {
  21. if (Interlocked.Decrement(ref _numerOfThreadsNotYetCompleted) == 0)
  22. _doneEvent.Set();
  23. }
  24. }
  25.  
  26. private static void firtstTest(int theadCount)
  27. {
  28. ThreadPool.SetMinThreads(2, 2);
  29. ThreadPool.SetMaxThreads(3, 3);
  30. for(var i = 0; i < theadCount; i++)
  31. {
  32. _content += nameof(firtstTest) + Environment.NewLine;
  33. ThreadPool.QueueUserWorkItem(ThreadPoolCallbackV1, (object)i);
  34. }
  35. _doneEvent.WaitOne();
  36. }
  37.  
  38. private void SubmitBtn_Click(object sender, EventArgs e)
  39. {
  40. _numerOfThreadsNotYetCompleted = 100;
  41. firtstTest(100);
  42. TextBox1.Text = _content + "All calculations are complete.";
  43. }
  44. }

參考網址: Solved: “The number of WaitHandles must be less than or equal to 64″

沒有留言:

張貼留言