C # thread pool for multithreading Article 3,
In the previous article C # thread pool of multithreading article 2, we mainly learned about the thread pool and concurrency and how to implement the cancellation option. In this article, we mainly learn how to use the wait handle and timeout, use the timer, and use the BackgroundWorker component.
5. Use the waiting handle and timeout
In this section, we will learn how to implement timeout and correct wait in the thread pool. The procedure is as follows:
1. Use Visual Studio 2015 to create a new console application.
2. Double-click to open the "Program. cs" file and write the code as follows:
1 using System; 2 using System. threading; 3 using static System. console; 4 using static System. threading. thread; 5 6 namespace Recipe05 7 {8 class Program 9 {10 // CancellationTokenSource: notification System. threading. cancellationToken to inform it of cancellation. 11 static void WorkerOperationWait (CancellationTokenSource cts, bool isTimedOut) 12 {13 if (isTimedOut) 14 {15 // convey the cancel request. 16 cts. cancel (); 17 WriteLine ("Worker operation timed out and was canceled. "); 18} 19 else20 {21 WriteLine (" Worker operation succeeded. "); 22} 23} 24 25 // CancellationToken: to disseminate a notification about cancellation. 26 // ManualResetEvent: notifies one or more threads that an event has occurred. 27 static void WorkerOperation (CancellationToken token, ManualResetEvent evt) 28 {29 for (int I = 0; I <6; I ++) 30 {31 // obtain whether the tag has been canceled. True if you have requested to cancel this flag; otherwise, false. 32 if (token. isCancellationRequested) 33 {34 return; 35} 36 Sleep (TimeSpan. fromSeconds (1); 37} 38 // set the event status to the terminated state, allowing one or more threads to continue. 39 evt. set (); 40} 41 42 static void RunOperations (TimeSpan workerOperationTimeout) 43 {44 using (var evt = new ManualResetEvent (false) 45 using (var cts = new CancellationTokenSource ()) 46 {47 // register a pending System. threading. waitHandle delegate, and specify a System. timeSpan value to indicate the timeout time. 48 // The first parameter: System. Threading. WaitHandle to be registered. Use System. Threading. WaitHandle instead of System. Threading. Mutex. 49 // second parameter: The System. Threading. WaitOrTimerCallback delegate called when the waitObject parameter is terminated. 50 // third parameter: the object passed to the delegate. 51 // The fourth parameter: System. TimeSpan indicates the timeout time. If timeout is 0 (zero), the function will test the object status and return immediately. If timeout is-1, the function's timeout interval never expires. 52 // fifth parameter: true indicates that after the delegate is called, the thread will no longer wait on the waitObject parameter; if false, indicates that the timer is reset after each wait operation is completed until the logout is completed. 53 // return value: encapsulate System. Threading. RegisteredWaitHandle of the local handle. 54 var worker = ThreadPool. registerWaitForSingleObject (evt, (state, isTimedOut) => WorkerOperationWait (cts, isTimedOut), null, workerOperationTimeout, true); 55 56 WriteLine ("Starting long running operation... "); 57 // ThreadPool. queueUserWorkItem: queues methods for execution. This method is executed when a thread pool thread becomes available. 58 // cts. Token: gets the System. Threading. CancellationTokenSource associated with this System. Threading. CancellationToken. 59 ThreadPool. queueUserWorkItem (_ => WorkerOperation (cts. token, evt); 60 61 Sleep (workerOperationTimeout. add (TimeSpan. fromSeconds (2); 62 63 // cancel by System. threading. threadPool. the registered pending operation issued by the RegisterWaitForSingleObject method. 64 worker. unregister (evt); 65} 66} 67 68 static void Main (string [] args) 69 {70 // timeout 71 RunOperations (TimeSpan. fromSeconds (5); 72 // implement wait 73 RunOperations (TimeSpan. fromSeconds (7); 74} 75} 76}
3. Run the console application. The running effect is shown in:
Another useful method is ThreadPool. registerWaitForSingleObject. This method allows us to put the callback method into the queue of the thread pool. When the provided waiting handle sends a signal or times out, this callback method is executed. This allows timeout for operations in the thread pool.
In the code of row 71st, we call the "RunOperations" method in the main thread and pass the value 5 to its workerOperationTimeout parameter, indicating that the timeout time is 5 seconds.
In the code of Row 3, we call the static method "RegisterWaitForSingleObject" of ThreadPool and specify the "WorkerOperationWait" method to be executed for the callback method. The timeout time is 5 seconds.
In the code of Row 3, we call the static method "QueueUserWorkItem" of ThreadPool to execute the "WorkerOperation" method, which consumes 6 seconds, timeout has been sent in the thread pool within these six seconds, so 13th ~ 18 lines and 32nd ~ Code at 35 rows.
In the code of Row 3, we passed the value 7 to the "RunOperations" method and set the thread pool timeout time to 7 seconds, because the "WorkerOperation" method is executed for 6 seconds, in this case, no timeout occurs and the "WorkerOperation" method is successfully executed.
6. Use a timer
In this section, we will learn how to use the System. Threading. Timer object to regularly call an asynchronous operation in the thread pool. The procedure is as follows:
1. Use Visual Studio 2015 to create a new console application.
2. Double-click to open the "Program. cs" file and write the code as follows:
1 using System; 2 using System. threading; 3 using static System. console; 4 using static System. threading. thread; 5 6 namespace Recipe06 7 {8 class Program 9 {10 static Timer timer; 11 12 static void TimerOperation (DateTime start) 13 {14 TimeSpan elapsed = DateTime. now-start; 15 WriteLine ($ "{elapsed. seconds} seconds from {start }. timer thread pool thread id: {CurrentThread. managedThreadId} "); 16} 17 18 static void Main (string [] args) 19 {20 WriteLine ("Press 'enter' to stop the timer... "); 21 DateTime start = DateTime. now; 22 // initialize a new instance of the Timer class, using System. timeSpan value to measure the time interval. 23 // The first parameter is a System. Threading. TimerCallback delegate, indicating the method to be executed. 24 // second parameter: an object that contains the information to be used by the callback method, or null. 25 // The third parameter: System. TimeSpan, which indicates the amount of time delay before the callback parameter calls its method. Specify-1 ms to prevent the timer from being started. Specify zero (0) to start the timer immediately. 26 // fourth parameter: The interval between the methods referenced by calling callback. Periodic termination can be disabled within 1 ms. 27 timer = new Timer (_ => TimerOperation (start), null, TimeSpan. fromSeconds (1), TimeSpan. fromSeconds (2); 28 try29 {30 Sleep (TimeSpan. fromSeconds (6); 31 // change the interval between the timer start time and the method call, using System. timeSpan value measurement interval. 32 // The first parameter: A System. TimeSpan, which indicates the amount of latency before the callback method specified when System. Threading. Timer is called. Specify negative-1 millisecond to prevent the timer from restarting. Specify zero (0) to immediately restart the timer. 33 // second parameter: The interval between the call of the callback method specified when System. Threading. Timer is constructed. Periodic termination can be disabled within 1 ms. 34 timer. change (TimeSpan. fromSeconds (1), TimeSpan. fromSeconds (4); 35 ReadLine (); 36} 37 finally38 {39 timer. dispose (); 40} 41} 42} 43}
3. Run the console application. The running effect may be different each time, as shown in:
First, we created a Timer instance. The first parameter of its constructor is a lambda expression, indicating the code to be executed in the thread pool, in this expression, we call the "TimerOperation" method and provide it with a start time value. Since we didn't use the state object, we passed null to the second parameter of the Timer constructor. The third parameter indicates that it takes 1 second to execute TimerOperation for the first time. The fourth parameter indicates that the interval between each call of "TimerOperation" is 2 seconds.
After the main thread is blocked for 6 seconds, we call the "Change" method of the Timer instance and Change the interval between each call of "TimerOperation" to 4 seconds.
Finally, we are waiting to Enter the "Enter" key to end the application.
7. Use the BackgroundWorker component
In this section, we will learn another asynchronous programming method: BackgroundWorker component. With the help of this component, we can organize our asynchronous code through a series of events and event handling methods. The procedure is as follows:
1. Use Visual Studio 2015 to create a new console application.
2. Double-click to open the "Program. cs" file and write the code as follows:
1 using System; 2 using System. componentModel; 3 using System. threading; 4 using static System. console; 5 using static System. threading. thread; 6 7 namespace Recipe07 8 {9 class Program 10 {11 static void WorkerDoWork (object sender, DoWorkEventArgs e) 12 {13 WriteLine ($ "DoWork thread pool thread id: {CurrentThread. managedThreadId} "); 14 var bw = (BackgroundWorker) sender; 15 for (int I = 1; I <= 100; I ++) 16 {17 // obtain a value indicating whether the application has requested to cancel background operations. 18 // true if the application has requested to cancel the background operation; otherwise, false. The default value is false. 19 if (bw. cancellationPending) 20 {21 e. cancel = true; 22 return; 23} 24 25 if (I % 10 = 0) 26 {27 // cause System. componentModel. backgroundWorker. progressChanged event. 28 // parameter: Percentage of completed background operations, ranging from 0% to 100%. 29 bw. ReportProgress (I); 30} 31 32 Sleep (TimeSpan. FromSeconds (0.1); 33} 34 35 // gets or sets the value representing the asynchronous operation result. 36 e. Result = 42; 37} 38 39 static void WorkerProgressChanged (object sender, ProgressChangedEventArgs e) 40 {41 // e. ProgressPercentage: Get the progress percentage of an asynchronous task. 42 WriteLine ($ "{e. progressPercentage} % completed. progress thread pool thread id: {CurrentThread. managedThreadId} "); 43} 44 45 static void WorkerCompleted (object sender, RunWorkerCompletedEventArgs e) 46 {47 WriteLine ($" Completed thread pool thread id: {CurrentThread. managedThreadId} "); 48 // e. error: gets a value that indicates an Error occurred during an asynchronous operation. 49 if (e. Error! = Null) 50 {51 // print the error message generated during the asynchronous operation. 52 WriteLine ($ "Exception {e. error. message} has occured. "); 53} 54 else if (e. cancelled) // gets a value indicating whether the asynchronous operation has been canceled. 55 {56 WriteLine ($ "Operation has been canceled."); 57} 58 else 59 {60 // e. Result: gets the value representing the asynchronous Operation Result. 61 WriteLine ($ "The answer is: {e. result} "); 62} 63} 64 65 static void Main (string [] args) 66 {67 // initialize System. componentModel. A new instance of the BackgroundWorker class. This class performs operations on a separate thread. 68 var bw = new BackgroundWorker (); 69 // gets or sets a value that indicates whether System. ComponentModel. BackgroundWorker can report progress updates. 70 // If System. ComponentModel. BackgroundWorker supports progress update, the value is true; otherwise, the value is false. The default value is false. 71 bw. WorkerReportsProgress = true; 72 // obtain or set a value that indicates whether System. ComponentModel. BackgroundWorker supports asynchronous cancellation. 73 // If System. ComponentModel. BackgroundWorker supports cancellation, the value is true; otherwise, the value is false. The default value is false. 74 bw. workersuppscanscancellation = true; 75 76 // An error occurred while calling System. ComponentModel. BackgroundWorker. RunWorkerAsync. 77 bw. DoWork + = WorkerDoWork; 78 // occurs when System. ComponentModel. BackgroundWorker. ReportProgress (System. Int32) is called. 79 bw. ProgressChanged + = WorkerProgressChanged; 80 // occurs when the background operation is completed, canceled, or an exception is thrown. 81 bw. RunWorkerCompleted + = WorkerCompleted; 82 83 // start to perform background operations. 84 bw. RunWorkerAsync (); 85 86 WriteLine ("Press C to cancel work"); 87 88 do 89 {90 // gets the next character or function key that the user presses. Press the key to display it in the console window. 91 // determine whether the press Key is displayed in the console window. If the value is true, the pressed key is not displayed; otherwise, the value is false. 92 if (ReadKey (true). KeyChar = 'C') 93 {94 // The request cancels the pending background operation. 95 bw. CancelAsync (); 96} 97} 98 // obtain a value indicating whether the System. ComponentModel. BackgroundWorker is running an asynchronous operation. 99 // If System. ComponentModel. BackgroundWorker is running an asynchronous operation, the value is true; otherwise, the value is false. 100 while (bw. IsBusy); 101} 102} 103}
3. Run the console application. The running effect may be different each time, as shown in:
At line 1 of code, we created an instance of the BackgroundWorker component and explicitly stated that the instance supports progress update and asynchronous cancellation operations in line 2 and line 3.
Three event handling methods are attached to the instance in the fields of 77th lines of code, 79th lines of code, and 81st lines of code. When a DoWork, ProgressChanged, or RunWorkerCompleted event occurs, the corresponding WorkerDoWork method, WorkerProgressChanged, and WorkerCompleted methods are executed.
For other code, see annotations.
Source code download