從上一篇可知ThreadPool的基本設定了,不過接下來又遇上新的問題。
- 如何確認所有執行緒完成?
- 將主控台專案改為WinForm專案使用ThreadPool...
首先,先說明如何確認所有執行緒完成工作?
在此我使用者WaitHandle.WaitAll()來確認。
依照官網的說明,必須將doneEvent = new ManualResetEvent(false)帶入ThreadPool,最終使用doneEvent陣列帶入WaitHandle.WaitAll()即可。
二話不說,先上程式碼。
internal class Program { static void Main(string[] args) { const int FibonacciCalculations = 10; firtstTest(FibonacciCalculations); } private static void ThreadPoolCallbackV1(object obj) { if(obj == null) return; ManualResetEvent doneEvent = (ManualResetEvent)obj; Console.WriteLine(nameof(ThreadPoolCallbackV1)); doneEvent.Set(); } private static void firtstTest(int theadCount) { ThreadPool.SetMinThreads(2, 2); ThreadPool.SetMaxThreads(3, 3); ManualResetEvent[] doneEvents = new ManualResetEvent[theadCount]; for(var i = 0; i < theadCount; i++) { doneEvents[i] = new ManualResetEvent(false); Console.WriteLine($"{nameof(firtstTest)} 第 {i} 次執行..."); ThreadPool.QueueUserWorkItem(ThreadPoolCallbackV1, doneEvents[i]); } WaitHandle.WaitAll(doneEvents); Console.WriteLine("All calculations are complete."); } }
是的,這個是主控台的程式碼,當所有執行緒完成後,才會印出最後一個句子。
接下來的問題,就是要把這段程式碼改道WomForm的專案了...
最剛開始,我想...在主控台都測通過了,沒有問題。於是,隨便開了個空的Form表單,就把程式碼貼過去了。
結果? 當然是失敗了...
我花了至少三天才搞定它,雖然解法非常簡單...
二話不說,先上程式碼。
Program.cs
internal static class Program { ////// The main entry point for the application. /// //[STAThread] 將STAThread改為MTAThread,WaitHandle.WaitAll不支援STAThread,改為MTAThread就能夠執行... [MTAThread] static void Main() { // To customize application configuration such as set high DPI settings or default font, // see https://aka.ms/applicationconfiguration. ApplicationConfiguration.Initialize(); Application.Run(new Form1()); } }
Form1.cs
private string _content = string.Empth; public partial class Form1 : Form { public TpcGrpcStressTest() { InitializeComponent(); } private static void ThreadPoolCallbackV1(object obj) { if(obj == null) return; ManualResetEvent doneEvent = (ManualResetEvent)obj; _content += nameof(ThreadPoolCallbackV1) + Environment.NewLine; doneEvent.Set(); } private static void firtstTest(int theadCount) { ThreadPool.SetMinThreads(2, 2); ThreadPool.SetMaxThreads(3, 3); ManualResetEvent[] doneEvents = new ManualResetEvent[theadCount]; for(var i = 0; i < theadCount; i++) { doneEvents[i] = new ManualResetEvent(false); _content += $"{nameof(firtstTest)} 第 {i} 次執行..." + Environment.NewLine; ThreadPool.QueueUserWorkItem(ThreadPoolCallbackV1, doneEvents[i]); } WaitHandle.WaitAll(doneEvents); } private void SubmitBtn_Click(object sender, EventArgs e) { firtstTest(10); TextBox1.Text = _content + "All calculations are complete."; } }
答案超簡單,但困擾我許久,特別記錄下來,免得下次又發生。
參考網址: AutoResetEvent.WaitAll 等到人生三大事,然后大笑开心。
本以為到此為止,但是當我測試超過100筆執行緒時,又一個問題出現了...
System.NotSupportedException: 'The number of WaitHandles must be less than or equal to 64.'
再去查了資訊,才發現原來超過64個執行緒使用WaitHandle.WaitAll會錯誤...
實際找到解法後,說明不需要使用WaitHandle.WaitAll來檢查程序是否執行完成,那麼二話不說,修改程式碼...
Program.cs
internal static class Program { ////// The main entry point for the application. /// [STAThread] static void Main() { // To customize application configuration such as set high DPI settings or default font, // see https://aka.ms/applicationconfiguration. ApplicationConfiguration.Initialize(); Application.Run(new Form1()); } }
Form1.cs
public partial class Form1 : Form { private string _content = string.Empth; private static int _numerOfThreadsNotYetCompleted = 0; private static ManualResetEvent _doneEvent = new ManualResetEvent(false); public TpcGrpcStressTest() { InitializeComponent(); } private static void ThreadPoolCallbackV1(object obj) { if(obj == null) return; try { ManualResetEvent doneEvent = (ManualResetEvent)obj; _content += $"{nameof(ThreadPoolCallbackV1)} 第 {(int)i} 次執行...") + Environment.NewLine; doneEvent.Set(); } finally { if (Interlocked.Decrement(ref _numerOfThreadsNotYetCompleted) == 0) _doneEvent.Set(); } } private static void firtstTest(int theadCount) { ThreadPool.SetMinThreads(2, 2); ThreadPool.SetMaxThreads(3, 3); for(var i = 0; i < theadCount; i++) { _content += nameof(firtstTest) + Environment.NewLine; ThreadPool.QueueUserWorkItem(ThreadPoolCallbackV1, (object)i); } _doneEvent.WaitOne(); } private void SubmitBtn_Click(object sender, EventArgs e) { _numerOfThreadsNotYetCompleted = 100; firtstTest(100); TextBox1.Text = _content + "All calculations are complete."; } }
參考網址: Solved: “The number of WaitHandles must be less than or equal to 64″