Recommendation 80: Replace ThreadPool with Task
ThreadPool has many advantages over thread, but there are some inconvenient threadpool in use. Like what:
ThreadPool does not support interactive operations such as thread cancellation, completion, failure notification, and so on.
ThreadPool does not support the prioritization of thread execution.
In the past, developers needed to do a lot of extra work if they wanted to implement the above features. Now, the FCL provides a more powerful concept: Task. The task online pool is optimized based on and provides more APIs. In FCL 4.0, if we were to write multithreaded programs, the task was obviously better than the traditional way.
The following is a simple example of a task:
Static voidMain (string[] args) {Task T=NewTask (() ={Console.WriteLine ("tasks begin to work ..."); //Simulation Work ProcessThread.Sleep ( the); }); T.start (); T.continuewith (Task)={Console.WriteLine ("the task is completed and the status of the completion time is:"); Console.WriteLine ("iscanceled={0}\tiscompleted={1}\tisfaulted={2}", Task. IsCanceled, Task. IsCompleted, Task. isfaulted); }); Console.readkey (); }
Task tasks have the following properties, which let us query the state of the task when it is completed:
IsCanceled was completed because of the cancellation.
IsCompleted completed successfully
Isfaulted completed because of an exception
It is important to note that the task does not provide a callback event to inform completion (like BackgroundWorker), which accomplishes a similar function by enabling a new task. The ContinueWith method can initiate a new task when a task is completed, which naturally supports the task's completion notification: We can get the result value of the original task in the new task.
Here is a slightly more complex example that supports the ability to complete notifications, cancel, get task return values, and more:
Static voidMain (string[] args) {CancellationTokenSource cts=NewCancellationTokenSource (); Task<int> t =Newtask<int> (() =Add (CTS. Token), CTS. Token); T.start (); T.continuewith (taskended); //wait to press any key to cancel the taskConsole.readkey (); Cts. Cancel (); Console.readkey (); } Static voidTaskended (task<int>Task) {Console.WriteLine ("the task is completed and the status of the completion time is:"); Console.WriteLine ("iscanceled={0}\tiscompleted={1}\tisfaulted={2}", Task. IsCanceled, Task. IsCompleted, Task. isfaulted); Console.WriteLine ("The return value of the task is: {0}", Task. Result); } Static intAdd (CancellationToken CT) {Console.WriteLine ("Task begins ..."); intresult =0; while(!Ct. iscancellationrequested) {result++; Thread.Sleep ( +); } returnresult; }
When you press the keyboard about 3 seconds after the start of the task, you get the following output:
Task begins ...
The task is completed and the status of the completion time is:
Iscanceled=false iscompleted=true Isfaulted=false
The return value of the task is: 3
You may be surprised that our task is handled by cancel, why the completed state iscanceled that column or false. Because in the work task, we deal with iscancellationrequested in business logic, but it is not handled by the Throwifcancellationrequested method. If you take the Throwifcancellationrequested method, the code should look like this:
Static voidMain (string[] args) {CancellationTokenSource cts=NewCancellationTokenSource (); Task<int> t =Newtask<int> (() =Addcanclebythrow (CTS. Token), CTS. Token); T.start (); T.continuewith (Taskendedbycatch); //wait to press any key to cancel the taskConsole.readkey (); Cts. Cancel (); Console.readkey (); } Static voidTaskendedbycatch (task<int>Task) {Console.WriteLine ("the task is completed and the status of the completion time is:"); Console.WriteLine ("iscanceled={0}\tiscompleted={1}\tisfaulted={2}", Task. IsCanceled, Task. IsCompleted, Task. isfaulted); Try{Console.WriteLine ("The return value of the task is: {0}", Task. Result); } Catch(AggregateException e) {e.handle (err)= Err isoperationcanceledexception); } } Static intaddcanclebythrow (CancellationToken CT) {Console.WriteLine ("Task begins ..."); intresult =0; while(true) {Ct. Throwifcancellationrequested (); Result++; Thread.Sleep ( +); } returnresult; }
Then the output is:
Task begins ...
The task is completed and the status of the completion time is:
Iscanceled=true iscompleted=true Isfaulted=false
In the method Taskendedbycatch of the task end evaluation, if the task ends with the Throwifcancellation requested method, Finding the result value for a task throws an exception operationcanceledexception, not the result value before the exception is thrown. This means that the task is canceled by way of exception, so you can notice that the status iscanceled is true in the output of the above code.
Then look at the output above, and we notice that the cancellation is implemented in an unusual way, and that the isfaulted state of the exception in the task is still false. This is because throwifcancellation requested is a method in the type CancellationTokenSource of the cooperative cancellation method, which the CLR has handled specially. The CLR knows that this is a program that the developer is interested in, so it is not considered an exception (it is understood to be canceled). To get the state that isfaulted equals true, we can modify the while loop to simulate an exception, as follows:
while (true) { //Ct. Throwifcancellationrequested (); if 5 { thrownew Exception ("error" ) ); } Result+ +; Thread.Sleep (+);
The output after the simulated exception is:
Task begins ...
The task is completed and the status of the completion time is:
Iscanceled=false iscompleted=true Isfaulted=true
The task also supports the concept of a mission factory. The task factory supports the sharing of the same state between multiple tasks, such as canceling type CancellationTokenSource that can be shared. By using a task factory, you can cancel a set of tasks at the same time:
Static voidMain (string[] args) {CancellationTokenSource cts=NewCancellationTokenSource (); //wait to press any key to cancel the taskTaskFactory TaskFactory =NewTaskFactory (); Task[] Tasks=Newtask[] {taskfactory.startnew ()=Add (CTS. Token), Taskfactory.startnew (()=Add (CTS. Token), Taskfactory.startnew (()=Add (CTS. Token)}; //Cancellationtoken.none indicates that tasksended cannot be canceledTaskfactory.continuewhenall (Tasks, tasksended, cancellationtoken.none); Console.readkey (); Cts. Cancel (); Console.readkey (); } Static voidtasksended (task[] tasks) {Console.WriteLine ("All tasks are complete! "); }
The output of the above code is:
Task begins ...
Task begins ...
Task begins ...
All tasks have been completed (canceled)!
This recommendation shows how task (Task) and TaskFactory (Task Factory) are used. Task further optimizes the scheduling of the background thread pool and speeds up the processing of threads. So in the FCL 4.0 era, if you want to use multi-threading, we should use task more.
Therefore, in the next recommendations of this book, if it is not particularly necessary, as long as the multi-threaded content is involved, the task will be used together.
Turn from: 157 recommendations for writing high-quality code to improve C # programs Minjia
157 recommendations for writing high-quality code to improve C # programs--recommendation 80: Replace ThreadPool with Task