Multi-thread asynchronous operations and multi-thread asynchronous operations

Source: Internet
Author: User
Tags apm

Multi-thread asynchronous operations and multi-thread asynchronous operations

Dedicated thread

Asynchronous operations restricted by computing

CLR thread pool, management thread

Task

Collaborative Cancellation

Timer

Await and async keywords

Asynchronous operations restricted by I/O

Windows asynchronous IO

APM (APM and Task)

EAP

Dedicated thread

When I first learned multi-Thread programming, the first step was how to open a new Thread, which is an instance of a new Thread. In Jreffy's book, this Thread is called as a dedicated Thread. It is different from the thread in the thread pool. Although both are the objects of the Thread class, the dedicated Thread cannot be used after it is used once. The Thread in the Thread pool can be used multiple times, wake up when a task is executed, sleep when it is idle, and sleep for a long time ends itself. Whether it is a dedicated thread or a thread in the thread pool, it is a CLR thread. a clr thread is a logical thread. On Windows, the CLR thread corresponds to a windows Thread. No matter what the thread is, it plays an indispensable role in asynchronous operations.

Thread Pool

The thread pool is introduced below. There are two types of threads in the thread pool: one is used to process computing restrictions and the other is to process IO restrictions. Generally, when we can directly call the thread pool thread to complete the operation, the thread used is the thread that calculates the operation restriction. Different from the old new Thread (), the Thread that gives an operation to the Thread pool does not receive any Thread instance externally, instead, this operation is encapsulated into a WorkItem and submitted to the thread pool as a request (that is, the QueueUserWorkItem method of the ThreadPool class is called). If a request is sent, a response is returned. Uncertain response time: if the thread pool has Idle threads, You can execute the work item immediately. If the thread pool has no Idle threads and the number of threads has not reached the upper limit, the Thread pool will create a new Thread to execute this work item. If there is no idle Thread and the number of threads has reached the upper limit, the Thread that submits the request to the Thread pool will be blocked, it is not until the idle thread in the thread pool receives the work item that the blocked thread recovers and the work item is processed.

Inside the thread pool, a picture in the CLR via C # book is stolen, and the thread (that is, the worker thread) for each computing restriction operation in the thread pool) both have a queue to store work items, and a global queue.

The work items submitted to the thread pool are placed in the global queue first. Then the idle worker thread will compete in the global queue to obtain a work item, and the obtained work item will adopt the first-in-first-out algorithm. The competing work items will be placed in the local queue, and the worker thread will obtain a work item in the local queue through an advanced algorithm for processing, assuming that there are no work items to be processed in the local queue, it will be processed in other local queues by competing with the first-in-first-out algorithm to obtain a work item. Of course, the latter will rarely appear. When all queues are empty, idle worker threads start to sleep.

Task

The Task class is introduced in. NET Framework4. It is actually an encapsulation of the thread pool call, but the Task is an asynchronous operation that becomes simpler, more intuitive, and better. There are three key objects in the Task category. One is the Task itself, representing the Task entity; the other is TaskFactory, which is used to build a Task; the other is TaskScheduler, it is used to schedule tasks and control the execution time of tasks. Next we will introduce them one by one.

You can enable a Task in the following ways:

new Task(action, "alpha").Start();Task.Run(()=>action("alpha"));Task.Factory.StartNew(action, "beta");

 

The last one is implemented using TaskFactory.

Tasks can block the current Thread to Wait for the completion of asynchronous operations, just by calling the Wait () method. Of course, there are many ways to Wait for completion. Wait simply waits for one completion. In addition, there are WaitAll waiting for all tasks to be completed and WaitAny waiting for a bunch of tasks to be completed. Tasks can obtain execution results, which is different from Thread and ThreadPool. Objects returned by asynchronous operations can be obtained through the Result attribute of the generic Task <TResult>, of course, to obtain the result, you must Wait for the execution to complete. Therefore, the calling thread will be blocked,

Task<Int32> t=new Task<Int32>(n=>Sum((Int32),),100000);t.Start();t.Wait();Console.WriteLine("The Sum is :"+t.Result);

 

If an exception occurs during Task execution, the exception is thrown when the Result attribute or Wait is called. The reason why both of them are called is that they are not necessarily called at the same time, sometimes only Wait is called, and sometimes only Result is called. For example, the following describes the ContinueWith method. Slightly modify the above method

Task<Int32> t=new Task<Int32>(n=>Sum((Int32),),100000);t.Start();t.ContinueWith(t=>Console.WriteLine("The Sum is :"+t.Result));

 

In this way, you do not need to call Wait to block the current thread and Wait for the Task to complete execution to get the result. What ContinueWith transfers is actually a callback. As the name suggests, it is to Wait for the Task to complete execution before executing the callback, of course, the callback is generally executed through the thread pool thread. Since ContinueWith returns a Task, you can call one or more ContinueWith as needed. This is the chain operation mentioned in JQuery. This writing method can also make the code more elegant, eliminating the need for nesting multiple callback layers in the traditional writing method. In fact, Microsoft's design is good, some custom callback operations can be used for reference. ContinueWith can reload a parameter with TaskContinuationOpetions enumeration. this parameter is specified to indicate that this callback is called only under certain conditions. For example, NotOnFaulted is executed when it is not a failure, while NotOnCanceled is executed when it is not canceled. Others can be obtained on MSDN.

In addition to the ContinueWith mechanism, a Task also provides a parent-child Task mechanism. All subtasks created in a Task are, parent tasks are automatically completed after all child tasks are executed. You do not need to explicitly call Wait.

Task<Int32 []> parent=new Task<Int32[]>(()=>{Var result=new Int32[3];New Task(()=>{result[0]=Sum(1000)},TaskCreationOperations.AttanchedToParent).Start();New Task(()=>{result[1]=Sum(2000)},TaskCreationOperations.AttanchedToParent).Start();New Task(()=>{result[2]=Sum(3000)},TaskCreationOperations.AttanchedToParent).Start();});Parent.ContinueWith(t=>Array.ForEach(t.Result,Console.WriteLine));Parent.Start();

 

Again, we copied the code of CLR via C. This code is used to show that the relationship between parent and child tasks is based on the AttanchedToParent value enumerated by TaskCreationOperations.

TaskFactory, as its name implies, is the factory for building a Task. It is convenient to create multiple tasks with the same settings. These settings include TaskCreationOpeartions, TaskContinuationOperations, CancellationToken, and TaskScheduler. At the same time, it is convenient to use the Continue method for each Task created by the factory. However, the worst price is that the NotOn and OnlyOn values of TaskContinuationOperations are invalid. If you need to use these values, you still need to traverse all tasks to call them one by one.

TaskScheduler is the logic that defines Task scheduling and determines when to execute and how to execute tasks. In FCL, two task schedulers are defined by default (but I see three in the source code of 4.6): ThreadPoolTaskScheduler (thread pool Task Scheduler) and SynchorizationContextTaskScheduler (synchronization context Task Scheduler), and ConcurrentExclusiveTaskScheduler. The thread pool Task scheduler is the default Task Scheduler. The default Task uses the thread pool because the scheduler is used, at the same time, the scheduler implements the storage and execution of tasks in various queues in the thread pool. The synchronization context Task Scheduler calls the UI threads in applications such as WinForm and WCF. From this point of view, the responsibility division of the Task system is relatively detailed. The Task contains the content of the Task. The Factory is responsible for constructing and executing the Task by Schedule, in this case, the architecture of the entire system does not need to be modified if it does not meet the requirements. In order to cite the role of TaskSchedule, we also made a small experiment. Let's take a look at the following code:

The Action causes the thread to sleep for 10 seconds and then outputs a message. This operation is executed on a dedicated thread. The default dedicated thread is a non-Background thread. The program can be executed only after it is executed.

Then, replace the dedicated thread with the Task for execution,

The program is executed without waiting for information output. This is because the default scheduler is the thread pool scheduler, And the thread pool thread is the background thread, as long as the main thread ends, the background thread ends no matter whether the execution is complete or not, so the information output cannot be seen. This means that if I specify a scheduler to be executed using a dedicated thread, the scheduler can sleep normally for 10 seconds, output the message, and end the running. For this reason, I defined a TaskScheduler,

Define a TaskFacotry to use the ThreadTaskScheduler

After the Task is executed, the Task can be executed on a dedicated thread and the information is output 10 seconds later. In this way, I also defined a scheduler similar to a scheduled call, but here I went a little wild, and the result was still effective.

In addition, the abstract class TaskScheduler must be inherited and three methods must be rewritten.

After reading the FCL source code, we can find that the Task is actually executed in the next two methods. If QueueTask is not executed, it will be executed again in TryExecuteTaskInline.

Collaborative Cancellation

This content is not used to execute an asynchronous operation in some way, but involves the execution process of an asynchronous operation, so it is mentioned. There are two ways to end a Thread that is being executed. One is to use the Abort method of the Thread. This method has two drawbacks: the end is uncontrollable, it cannot be ensured that the Thread is actually ended or where it is ended; only the object of the Thread can be called. Another way is to set an identifier variable in a key position, which indicates whether the current operation should end. If the external end is required, the value of this variable can be changed. This method is not applicable, and the thread synchronization problem often needs to be handled by the user each time. However, this is also a collaborative cancellation. The CancellationTokenSource class is provided in FCL to implement this mode. The usage is simple. You can call the Cancel () method where you want to Cancel the operation. How can you determine whether the operation has been canceled during execution? The Token attribute of CancellationTokenSource returns a struct of the CancellationToken type. For example, if the struct of the CancellationToken is used in the Task, you can call the IsCancellationRequested attribute of the struct to check whether the operation has been canceled. The same object needs to control whether it needs to end in multiple places. You can obtain the Token from the same CancellationTokenSource. Because of laziness, you copy the routine.

Timer

It is certainly no stranger to the application scenario that requires regular or periodic operations. The wild path is a new Thread, and then an endless loop is executed in it, and the duration of this cycle is calculated in advance, then, the operation and Sleep are continuously performed in the endless loop. There are a lot of Timer in FCL, but Jeffrey recommends System. Threading. Timer. One of the constructors of this class is as follows:

public Timer(TimerCallback callback, object state, int dueTime, int period);

 

Callback is a scheduled operation, and dueTime is the delay time for the first execution. specifying System. Threading. Timeout. Infinite can prevent the timer from being started. Specify zero (0) to start the timer immediately. The call interval (in milliseconds ). If System. Threading. Timeout. Infinite or-1 is specified, periodic termination can be disabled, that is, it will be called only once. Once the Timer is constructed, it will start running immediately (not to execute callback, because there is dueTime)

If you need to Change the execution cycle, you can use the Change method.

public bool Change(int dueTime, int period);

 

That is to say, you can use this Timer to specify an operation to be executed at a certain time or to execute it repeatedly.

Async and await keywords

The async declaration method contains an asynchronous call. The return value is void, Task, or Task <Tresult>. On MSDN, This Is An Asynchronous Method.

The await keyword is used in the method with aysnc declaration. The method that uses this keyword must return Task or Task <Tresult>, rather than void. This indicates that if the actual method to use await does not need to return the results of some operations (or operations), the Task is returned; otherwise, the result must be returned, task <Tresult> is used. The statement after a statement with the await keyword is executed in a different thread from the statement before await. That is, in the async method, the threads before and after await execution are different. Otherwise, the whole method is executed by the same thread without await, And the thread is the thread that calls this method.

Using this code to cite the above points,

In TestMain, PrintThreadId, and GetValueAsync, the thread Id is printed respectively. The GetValueAsync method uses the Task, which must be different from the TestMain thread Id. In PrintThreadId, The await keyword is not used, therefore, call the GetValueAsync thread and immediately execute the following WriteLine method. The running result is as follows:

The above code is slightly modified.

In this way, the thread will return immediately from the PrintThreadId Method after calling await. The output thread IDs of Main Method and Async Method 1 are consistent, the thread Id output by Async Method 2 is inconsistent.

Public async void DisplayValue () {double result = await GetValueAsync (1234.5, 1.01); // a new thread is enabled to process the GetValueAsync task, then, all the code after the method returns // will be encapsulated as a delegate, and the System will be called when the GetValueAsync task is completed. diagnostics. debug. writeLine ("Value is:" + result );}

 

The above code is equivalent to the following code, System. diagnostics. debug. writeLine ("Value is:" + result); is put into a delegate. The delegate is called only after the asynchronous code in GetValueAsync is executed.

public void DisplayValue(){      System.Runtime.CompilerServices.TaskAwaiter<double> awaiter = GetValueAsync(1234.5, 1.01).GetAwaiter();      awaiter.OnCompleted(() =>      {            double result = awaiter.GetResult();            System.Diagnostics.Debug.WriteLine("Value is : " + result);      });}

 

IO restrictions

Perform IO operations in Windows

The following two images show how to perform synchronous and asynchronous IO operations in Windows.

For example, if Windows executes a synchronous IO process, the Read method of FileStream is called first, and the Win32 ReadFile function is called internally, then, the operation will be encapsulated into an IO Request Package (IRP), and then the kernel method will be called, the kernel method puts the IRP in an IRP queue of the corresponding IO Device. This queue is maintained independently by each IO device, and the IPR is put in the queue and waiting for processing by the device, at this time, the thread will be blocked (in fact, it is not known whether it is blocked or sleep, because the text in the book is written in sleep, but the picture is written in blocking ), after the processing is completed, the system will return the result step by step.

Asynchronous IO is roughly the same as the first four steps of synchronous IO. The minor difference is that the first step calls ReadAsync. When IRP is put into the IP queue, the thread can immediately return and do other things to avoid being silly. When the device processes this IRP, it records an IO-completed callback. At this time, it can send a request to the thread pool to execute this callback, i/O threads in the thread pool should be called here.

APM

APM is actually short for asynchow.us Programming Model (asynchronous Programming Model). In normal coding, we will find that some methods are prefixed with BeginXXX and EndXXXX, which is the legendary APM. The difference with synchronization is that after the BeginXXX method is called, it will return immediately and will not block the current thread. After the call is completed, it needs to call the EndXXX method to obtain the call result, this method should be executed in the callback method, because if the asynchronous call is not completed, EndXXX will block the calling thread, and EndXXX must also be called, otherwise, the asynchronous operation will occupy a thread in the thread pool, and the call to this thread will be white and wasted resources. Classes that support APM include System. IO. Stream and Its Derived classes, System. Net. Sockets. Socket, System. Net. WebRequest, and Their Derived classes. However, the APM operations in System. IO. Stream are not actually asynchronous IO operations. In addition, Action also provides APM, but it does not require asynchronous IO operations.

As shown in the preceding routine, all BeginXXX methods need to pass in the callback of AsyncCallback and an object-type state parameter in addition to some parameters of the original method, and return an IAsyncResult object, however, this object generally does not need to be ignored. It will be passed in the AsyncCallback method. In the IAsyncResult object, there is an AsyncState attribute, and the obtained object is the State object passed in, when you call the EndXXX method, you also need to upload the IAsyncResult object. If the original method returns results, the returned results are obtained from EndXXX. If an exception occurs during the asynchronous operation, the exception will be thrown when the EndXXX method is called. If the EndXXX method is not called, this exception will directly cause the CLR to collapse and cause the program to exit directly.

Because the Task implements the IAsyncResult interface, it also provides some support for APM. The following code segment shows how to use the Task to implement APM.

EAP

EAP is short for Event-based Asynchronous Pattern (Event-based Asynchronous Pattern). There are different opinions on the advantages and disadvantages of this Pattern. Microsoft has an article on its official website praising this Pattern, jeffrey, however, approves this mode. At least in Socket programming, EAP will be superior to APM.

In EAP mode, an asynchronous call is started through the XXXAsync method, and the XXXCompleted event is triggered after the asynchronous call is completed.

EAP is also supported in tasks.

EAP exceptions are not thrown. to check whether exceptions occur in asynchronous calls, you must check whether exceptions are null in the Exception attribute of AsyncCompletedEventArgs. To determine the Exception type, you must use if and typeof, it is not a catch Block. If exceptions are not taken care of, the program can continue to run. I remember comparing APM and EAP at that time, saying that every time APM generates an IAsyncResult object, it will consume memory, and the EventArgs of EAP can be reused.

In fact, Jeffrey's example in the book is more appropriate, because he uses the WebClient class, the Complete event is in the WebClient, And the Socket Complete is in the Arg event parameter, socket is a special case for implementing EAP. It is no wonder that the EAP-supported classes listed by Jeffrey do not have it. The event parameters of other EAP-supported classes inherit from AsyncCompletedEventArgs,

You can use the Error attribute to check whether an exception has occurred in an asynchronous call, or Cancelled to check whether the call has been canceled. However, the results obtained by different asynchronous calls are different, the result is defined in their inheritance class, for example

Compared with Socket, all asynchronous operations use the same event Parameter

An exception query is not an exception class, but a SocketError enumeration. the result obtained from different asynchronous operations must be obtained from different attributes.

Related Article

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.