C # Thread---task (Task) and thread pool have to say the secret

Source: Internet
Author: User

What we need to know is that there are many limitations to the QueueUserWorkItem technology. The biggest problem is that there is no built-in mechanism to let you know when the operation is complete, nor is there a mechanism to get a return value when the operation is complete, which makes it hard for us to enable this technology.

Microsoft has introduced the concept of task (tasks) to overcome these limitations, while addressing some other issues. Incidentally, we have to use them through the System.Threading.Tasks namespace.

Now I'm going to say that using the thread pool instead of calling ThreadPool's QueueUserWorkItem method is doing the same thing with the task:

1         static void Main (string[] args) 2         {3             Console.WriteLine ("Main thread Start"); 4             //threadpool.queueuserworkitem (startcode,5); 5             New Task (Startcode, 5). Start (); 6             Console.WriteLine ("Main thread run to this!") "); 7             Thread.Sleep (+); 8         } 9         private static void Startcode (Object i)         {             Console.WriteLine (" Start executing child threads ... {0} ", i);             thread.sleep (1000);//Analog code operation         }15     }

Hey, you'll find the results are the same.
Let's see what this is:

TaskCreationOptions This type is an enumeration type that passes some flags to control how the task is executed. TaskCreationOptions is defined as follows:

Slow down, the comments are very detailed, look at these benefits, TaskScheduler (Task Scheduler) do not know, please continue to look down, I will introduce, but please note that these are only a few proposals, when scheduling a task, may or may not adopt these proposals, One note, however: the AttachedToParent flag, which is always accepted by the task, because it has nothing to do with TaskScheduler itself.

Take a look at this piece of code:

1         static void Main (string[] args) 2         {3              4             //1000000000 This number will be thrown system.aggregateexception 5  6             task<int32> t = new task<int32> (n + = Sum ((Int32) n), 1000000000); 7  8             //Can start now, can also start later  9             t.start ();             //wait explicitly waits for a thread to complete the             t.wait ();            16             Console.WriteLine ("The Sum is:" +t.result);         private static Int32 sum (Int32 i)         {             Int32 sum = 0;22 for             (; i > 0; i--)                 checked { sum + = i; }24             return sum;25         }26     }

This code should be guessed what it means, everyone will write.
However, my result is why T.  Result instead of the sum that is returned directly? Is there a feeling of superfluous?

Let me say this code I want to express the meaning:

When a thread calls the Wait method, the system checks to see if the thread is waiting for the task to start executing, and if the task is executing, the Wait method will block the thread until the task runs to the end.

Say the above program execution, because the accumulated number is too large, it throws an arithmetic operation overflow error, when a calculation limit task throws an unhandled exception, this exception will be "contained" not and stored in a collection, and thread pool thread is allowed to return to the thread pool, When the wait method or the result property is called, the member throws a System.aggregateexception object.

Now you ask, why call wait or result? Or have you never queried the exception property of a task? Your code will never notice the occurrence of this exception, if you can not catch the exception, garbage collection, throw aggregateexception, the process will immediately terminate, this is "pull the whole body", the inexplicable program on its own off, no one knows what this situation. Therefore, you must invoke one of the previously mentioned members to ensure that the code notices the exception and recovers from the exception. Quietly tell you, in fact, when using result, the internal will call wait.

How to recover?

To help you detect an unnoticed exception, you can give a callback method to the static Unobservedtaskexception time level of the TaskScheduler, and when the task is garbage collected, the CLR finalizer raises the event if there is an exception that is not noticed. Once thrown, a Unobservedtaskexceptionevenargs object is passed to your time processor method, which contains the aggregateexception that you did not notice. Then call Unobservedtasexceptionevenargs's Setobserved method to indicate that your exception has been handled, thereby preventing the CLR from terminating the process. This is an easy way to do this, do less, prefer to terminate the process, do not stay in a state of corruption and continue to run. Life is also the same, sick would rather rest, do not go to work with the disease, you are not so great, the company does not need you this point of greatness, the fate of their own. (─.─| | | Pulled away.

In addition to a single wait task, the task provides two static methods: WaitAny and WaitAll, which allow the thread to wait for an array of task objects.

The WaitAny method blocks the calling thread, knowing that any one of the task objects in the array is complete, and this method returns an index value indicating which task object is completed. If a timeout occurs, the method returns-1. It can be canceled by a cancellationtoken and will throw a operationcanceledexception.

The WaitAll method also blocks the calling thread, knowing that all the task objects in the array are complete and returns true if all is done, and returns False if the timeout is exceeded. Of course it can also be canceled, the same will throw operationcanceledexception.

Say so two ways to cancel a task, now try this method, deepen the impression, modify the previous example code, the complete code is as follows:

1 static void Main (string[] args) 2 {3 CancellationTokenSource cts = new Cancellationtokensou Rce (); 4 5
6 7 task<int32> t = new task<int32> (() = Sum (cts. token,10000), CTS. Token); 8 9//can start now, or you can start at T.start (); 12 13//At a later time, cancel CancellationToken Source to cancel the Task14 CTS. Cancel ();//This is an asynchronous request, the task may have been completed. I'm a dual-core machine, task has not been completed 16 17 18//Note this to test thrown exception//console.writeline ("This sum is:" + t.result); 20 Try21 {22//If the task has been canceled, result will be thrown AggregateException23 Console.writelin E ("This sum is:" + t.result);}26 catch (AggregateException x) 27 {28 Treats any OperationCanceledException object as handled. 29//Any other exception causes a aggregateexception to be thrown, where 30//contains only unhandled exceptions. X.handle (e =&gt ; E is OperationCanceledException), Console.WriteLine ("Sum was Canceled"); 34}35 36 }37-Private static Int32 Sum (CancelLationtoken CT, Int32 i) (Int32) sum = 0;41 for (; i > 0; i--) 42 {43 If you call//cancel on a cancellationtokensource that is referenced by the cancellation flag, the following line will be thrown OperationCanceledException45 46 Ct. Throwifcancellationrequested (); checked {sum + = i;} }50 return sum;52}53}

This example shows an operation that was canceled halfway through a task, which I think is interesting and you'll find out if you try.
Lamada expression writes this, is a bright spot, must study, will cancellationtoken the closure variable "passes".

  

If you don't use the Lamada expression, the problem is really hard to solve:

task<int32> t = new task<int32> (() = Sum (cts. token,10000), CTS. Token);

Sum (CTS. token,10000) tokens need to be associated with Cts.token, can you figure out how to relate them?

  

OK, the mission canceled also talked about playing, to see a better use of technology:

  

1         static void Main (string[] args) 2         {3  4             task<int32> t = new task<int32> (i = Sum (Int32) i), 10000); 5  6             //Can start now, can also start later  7              8             T.start (); 9            Task cwt =  T.continuewith (task=> Console.WriteLine ("The Sum is:{0}", Task. Result),            cwt. Wait ();         }14         private static Int32 sum (Int32 i)-         {             Int32 sum = 0;18 for             (; i > 0 ; i--)             {                 checked {sum + = i;}             }22             return sum;24         }25     }

ContinueWith? What's the thing??

 to write scalable software, you must not make your thread blocked. This means that if you call wait or query the result property when the task is not completed, it is very likely that the thread pool will create a new thread, which increases resource consumption and impairs scalability.

ContinueWith is a better way to start another task when a task is completed . The above example does not block any threads.

When the sum task is complete, the task launches another task to display the results. ContinueWith will return a reference to the new Task object, so in order to see the result, I need to call the wait method, and of course you can query for result, or continue continuewith, the object returned can be ignored, it is just a variable.

Also note that the Task object contains a collection of continuewith tasks inside. So, you can actually use a task object to call ContinueWith multiple times. When the task is complete, all continuewith tasks go into the thread pool queue, and when we construct the continuewith we can see a Taskcontinuationoptions enumeration value, and we can't ignore it and look at its definition:

Preferefairness is as fair as possible, is that the earlier scheduled task may run earlier, first served, put the thread into the global queue, you can achieve this effect.

Executesynchronously refers to synchronous execution, forcing two tasks to run the same thread one after the other and then running synchronously.

Does it look dizzy? There are so many enumeration examples, how to master ah? More than a few times, know the use of the task, later use up handy ~ want to learn new technology, will be able to live, to the foundation of a firm . Take a look at an example, using these enumerations.

 1 static void Main (string[] args) 2 {3 task<int32> t = new task<int32> (i = S) Um ((Int32) i), 10000); 4 5 T.start (); 6 7 T.continuewith (Task=>console.writeline ("The Sum is:{0}", Task. Result), 8 taskcontinuationoptions.onlyonrantocompletion); 9 T.continuewith (Task=>console.writeline ("Sum Throw:" +task. Exception), taskcontinuationoptions.onlyonfaulted T.continuewith (task=> Console.WriteLine ("Sum was cancel:" +task.                 IsCanceled), taskcontinuationoptions.onlyoncanceled); TRY16 {17  T.wait (); Test}19 catch (AggregateException) (Console.WriteLine) ("Error");             }23}26 private static Int32 Sum (Int32 i) 28 {29 Int32 sum = 0;30 for(; i > 0; i--) {checked {sum + = i;} }34 return sum;36}37}

ContinueWith is done. But it's not over yet.

Attachedtoparnt enumeration type (parent task) also can't let go! Look at how to use, writing a little novelty, see:

 1 static void Main (string[] args) 2 {3 task<int32[]> parent = new Task<int32[]> ( () = {4 var results = new Int32[3]; 5//6 new Task (() = results[ 0] = Sum (10000), taskcreationoptions.attachedtoparent). Start (); 7 new Task (() = results[1] = Sum (20000), taskcreationoptions.attachedtoparent). Start (); 8 New Task (() = results[2] = Sum (30000), taskcreationoptions.attachedtoparent). Start (); 9 return results;10}); var cwt = parent. ContinueWith (Parenttask=>array.foreach (parenttask.result,console.writeline)); Arent. Start (); cwt.             Wait ();}18 private static Int32 sum (Int32 i) {Int32 sum = 0;22 for (; i > 0; i--): {checked {sum + = i;} }26 return sum;27}28} 

Oh, I've been writing dizzy ... (+﹏+) ~
example, the parent task creator initiates 3 task objects. By default, the task object created by one of the tasks is a top-level task that has no relation to the task that created them.

The TASKCREATIONOPTIONS.ATTACHEDTOPARENT flag Associates a task with the task that created it, unless all subtasks (subtasks of subtasks) end up running, otherwise the Create task (parent task) is not considered to be finished. When you call the ContinueWith method to create a task, you can specify the TASKCONTINUATIONOPTIONS.ATTACHEDTOPARENT flag to pin the continuation task as a subtask.

Looking at so many of the tasks of the method operation example, now to dig the task inside the structure :

Each Task object has a set of fields that make up the status of the task.

    • A Int32 ID (read-only property)
    • A Int32 that represents the execution state of a task
    • A reference to the parent task
    • A reference to the pinned Taskschedule when a task is created
    • A reference to the callback method
    • A reference to the object to be passed to the callback method (queried through the task read-only AsyncState property)
    • A reference to a Exceptioncontext
    • A reference to a ManualResetEventSlim object

There is not a single Task object that has a reference to some of the supplemental states that are created as needed, and the supplemental states contain these:

    • A CancellationToken
    • A collection of Continuewithtask objects
    • A collection of task objects prepared for the child task that throws an unhandled exception

Said so much, just want everybody to know:

Although the task provides a lot of functionality, it is not without cost. Because you must allocate memory for all of these states.

If you do not need the additional functionality provided by the task, using ThreadPool.QueueUserWorkItem, the resource will be used more efficiently.

The task class also implements the IDispose interface, which allows you to call Dispose after the task object is exhausted, but most of the garbage collector recycles it.

When you create a Task object, a Int32 field that is unique to the task is initialized to zero, and TaskID starts at 1 and increments by 1 for each assigned ID. Incidentally, when you look at a task object in your debugging, it causes the debugger to display the task ID, which causes the task to be assigned an ID.

The meaning of this ID is that each task can be identified by a unique value. Visual Studio displays these task IDs in its parallel tasks and parallel stacks windows. To be sure, this is the ID assigned by Visual Studio itself, not the ID assigned in your own code, and it is almost impossible to associate the ID assigned by Visual Studio with what the code is doing. To see the task that you are running, you can view the static CurrentID property of a task while debugging, and CurrentID returns null if no task is executing.

Then look at the value of TaskStatus, which can query the lifetime of a Task object:

These can be found at the time of the mission, and the judgment should look like this:

1 if (task. Status==taskstatus.rantocompletion) ...

To simplify coding, the task provides only a few read-only Boolean properties: Iscanceled,isfaulted,iscompleted, which return to the final state true/false.
If a task is created by calling a function, the Task object will run automatically because it is in the waitingforactivation state.

Finally, we're going to look at TaskFactory (Mission Factory):

 1. Need to create a set of Task objects to share the same state

2. To avoid mechanically passing the same parameters to each task's constructor.

By meeting These conditions, you can create a task factory to encapsulate the common state. TaskFactory types and taskfactory<tresult> types, both of which derive System.Object.

You'll learn a different way of coding:

 1 static void Main (string[] args) 2 {3 Task parent = new Task (() = 4 {5 var cts = new CancellationTokenSource (); 6 var tf = new Taskfactory<int32> (CTS. Token, Taskcreationoptions.attachedtoparent, taskcontinuationoptions.executesynchronously, TaskScheduler.Default) ; 7 8//Create and start 3 subtasks 9 var childtasks = new[] {ten tf. StartNew (() = Sum (cts. Token, 10000)), one TF. StartNew (() = Sum (cts. Token, 20000)), TF. StartNew (() = Sum (cts. Token, Int32.MaxValue))//This will throw an exception 13};14 15//Any sub-task throws an exception to cancel the remaining subtasks for (Int32 ta SK = 0; Task < Childtasks.length; task++) Childtasks[task]. ContinueWith (t = cts. Cancel (), taskcontinuationoptions.onlyonfaulted); 18 19//After all subtasks have been completed, the task gets the maximum value returned 20/not canceled /Then pass the maximum value to another task to display the maximum result of TF. ContinuewhenalL (childtasks,22 completedtasks = completedtasks.where (t =!t.isfaulted &&!t.iscanceled ). Max (t = t.result), Cancellationtoken.none) 24. ContinueWith (t = Console.WriteLine ("The Maxinum is:" + t.result), TaskContinuationOptions.Exe cutesynchronously). Wait (); Wait is used for test 26}); 27 28//After the child task finishes, any unhandled exception is also displayed in parent. ContinueWith (P =>30 {31//output all StringBuilder SB = new S with StringBuilder) Tringbuilder ("The following exception (s) occurred:" + Environment.NewLine); N.flatten (). innerexceptions) + sb. Appendline ("" + E.gettype (). ToString ()); Console.WriteLine (sb.) ToString ()); PNs}, taskcontinuationoptions.onlyonfaulted); 38 39//Start parent task.  Start (); try43 {44               Parent. Wait (); Showing results for}46 catch (aggregateexception),}49}50 rivate static Int32 sum (CancellationToken ct, Int32 N) The sum of the Int32 for (; n ; 0; n--) (CT). Throwifcancellationrequested (); checked {sum + = n;} }59 return sum;60}61}

The task factory is used as a collection of tasks.

Now look at TaskScheduler (Task Scheduler)

The task infrastructure is flexible, and theTaskScheduler object is a must.

The TaskScheduler object is responsible for executing the scheduled task while exposing the task information to the Visual Studio debugger, like a bridge that allows us to control our own task threads.

TaskScheduler has two derived classes: Thread pool Task Scheduler (thread pool tasks scheduling), and synchronization Context Task Scheduler (Synchronization Context Task Scheduler). By default, the application is using the thread pool Task Scheduler, which dispatches the task to the worker thread of the thread pool. You can query the static default property of TaskScheduler to get a reference to the default Task Scheduler.

The synchronization Context Task Scheduler is typically used for desktop applications, WINFROM,WPF, and Silverlight. This Task scheduler dispatches many tasks to the GUI thread of the application, enabling all task code to successfully update the UI build, such as buttons, menu items, and so on. The synchronization Context Task Scheduler does not use thread pooling at all. Similarly, you can query the static Fromcurrentsynchronizationcontext method of TaskScheduler to obtain a reference to a synchronization context Task Scheduler.

Just like this, create a type:

  

1//Synchronization Context Task Scheduler 2 TaskScheduler m_synccontexttaskscheduler =3            taskscheduler.fromcurrentsynchronizationcontext ();

C # Thread Article---Task (Task) and thread pool have to say the secret

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.