C # Asynchronous Programming (i) threading and asynchronous Programming fundamentals

Source: Internet
Author: User

Recently tried to do a few. NET core demo, read some source code, feel asynchronous programming in the core has become mainstream, and this piece I have not a systematic summary, so there is this text, next several articles, I will summarize the idea of asynchronous programming, the main reference CLR via C # And good blog posts that I've seen before. The first piece of text, we come together to lay the foundation, the thread of basic knowledge to comb over.

  This article is completely original, if reproduced please indicate the original author and link.

First, the thread base

Each thread has the following elements

Thread kernel objects (thread Kernael object)

The OS allocates and initializes this data structure for each thread created in the system, contains a set of properties that describe the thread, and also contains the so-called line Chengshan context (thread context). A context is a block of memory that contains a collection of CPU registers. For the x 86,x64 and arm CPU architectures, the thread context uses 700,1240 and 350 bytes of memory, respectively.

Thread environment block (thread environment BLOCK,TEB)

TEB is a block of memory that is allocated and initialized in user mode (the address space that the application code can quickly access). TEB consumes 1 pages of memory. TEB contains the top of the thread's exception handling chain. Each try block entered by the thread inserts a node at the beginning of the chain, and the thread exits the try block when the node is removed from the chain. Also included are thread-local storage data for threads, and some data structures used by GDI (graphics device interface, graphic devices interface) and OpenGL graphics

User mode stack (user-mode stack)

The user-mode stack stores local variables and arguments passed to the method. He also contains an address: indicates where the thread should proceed from when the current method returns. Windows defaults to allocating 1MB of memory to each thread's user-mode stack. More specifically, winows only retains the 1MB address space, and the physical memory is allocated only when the thread actually needs it.

Kernel-mode stack (Kernel-mode stack)

The so-called kernel mode, mainly core operating system components run in kernel mode, many drivers run in the core mode, the kernel mode is more efficient, if the kernel-mode driver corruption, the entire operating system will be corrupted.

The kernel-mode stack is also used when application code passes arguments to kernel-mode functions in the operating system. For full consideration, Windows copies the user-mode stack from the thread to the thread's kernel-mode stack for any arguments passed to the kernel from user-mode code. Once assigned, the kernel can validate the value of the argument. Because the application code cannot access the kernel-mode stack, the application cannot change the validated argument value. The 32-bit Windows kernel stack size 12kb,64 bit Windows is 24kb.

DLL thread connection (attach) and thread detach (detach) notifications

One of the policies of Windows is that whenever a thread is created in a process, the DllMain method of all unmanaged DLLs loaded in the process is called, and the Dll_thread_attach flag is passed to the method. Similarly, any time a thread terminates, it invokes the DllMain method of all unmanaged DLLs in the process and passes the DLL_THREAD_DETACH flag to the method. Some DLLs need to acquire these comrades to perform special initialization or (resource) cleanup operations for each thread created/destroyed in the process.

1.1 Windows System thread switching

A single core of the CPU can only perform one thread at a time (regardless of Intel Hyper-Threading Technology), and the executing thread may run a "time slice" (quantum, also called "quantum"). The time slice time slice expires, and the actions that Windows performs for thread switching:

1. Save the value of the CPU register in a context structure inside the kernel object of the currently running thread.

2. Select a thread from the existing thread collection to execute. (If the thread is owned by another process, Windows must also switch the virtual address space to the corresponding process before it starts executing any code)

3. Load the values in the selected execution thread context structure into the CPU register

While we are looking at the three steps above, the performance impact of thread switching may be more than three steps in actual execution. For example, the CPU now executes a different thread, and the code and data for the previous thread are still in the CPU's tell cache, which eliminates the need for the CPU to access RAM frequently. And once the context switches to the new thread, the new thread has a large probability of executing different code, accessing different data, and the code and data are not being told in the cache, so the CPU must access RAM to populate his cache.

1.2 Priority of Thread scheduling

Windows is called a preemptive multi-threaded (preemptive multithreaded) operating system because threads can stop (preempt) at any time and dispatch another thread. Programmers have some control over this, though not much. Remember that you cannot guarantee that your thread is running and you cannot prevent other threads from running.

In Windows, each thread is assigned a 0 (lowest) to 31 (highest priority), and the system decides which thread to allocate to the CPU, which is dispatched in a rotational manner.

The priority of a thread is calculated after the process priority and the thread itself is superimposed, such as.

Second, asynchronous programming 2.1 CLR thread pool

Creating and destroying threads is an expensive operation, and in order to improve the situation, the CLR contains code to manage its own thread pool (thread pool). One thread pool per CLR: This thread pool is shared by all the AppDomain controlled by the CLR.

The thread pool specifically maintains how many threads depend on the frequency of the program's request, and this CLR has an internal algorithm that we don't discuss in depth here.

2.2 Cancellation of asynchronous operations

Cancellation of an asynchronous operation can use the CancellationTokenSource class

A simple code example is as follows

Internal Static classcancellationdemo{ Public Static voidGo () {CancellationTokenSource cts=NewCancellationTokenSource (); ThreadPool.QueueUserWorkItem (o= = Count (cts. Token, +)); Console.WriteLine ("Press <enter> to cancel the operation");        Console.ReadLine (); Cts. Cancel ();//if the Count method has been returned, cancel has no effectConsole.ReadLine (); }    Private Static voidCount (CancellationToken token,intCountto) {         for(intCount =0; Count < Countto; count++)        {            if(token.) iscancellationrequested) {Console.WriteLine ("Count is cancelled");  Break;            } Console.WriteLine (count); Thread.Sleep ( $); } Console.WriteLine ("Count is done"); }}

2.3 Task

QueueUserWorkItem has no built-in mechanism to let you know when the operation is complete, and there is no mechanism to get the return value when the operation is complete. To overcome these limitations (and resolve some other issues), Microsoft introduces the concept of tasks.

The method is called as follows:

+ += {Console.WriteLine (+);}, CTS. Token);
2.3.1 Task Internal decryption

Each Task object has a set of fields that form the state of the task. This includes a Int32 Id (read-only), a int32 that represents the execution state of the task, a reference to the parent task, a reference to the TaskScheduler specified when the task was created, a reference to the callback method, A reference to the object to be passed to the callback method (class through the read-only AsyncState property query for the task), a reference to ExecutionContext, and a reference to the ManualResetEventSlim object. In addition, each Task object has a reference to create supplemental states as needed. Supplemental states contain CancellationToken, a collection of Continuewithtask objects, a set of task objects prepared for subtasks that do not throw unhandled exceptions, and so on. With so much more, let's realize that task is useful, but it's not without a price, and if you don't need the additional functionality of the task, then use ThreadPool. QueueUserWorkItem can achieve better resource utilization.

During the existence of a Task object, the read-only status property of the class query task knows where it is in its lifetime. This property returns a TaskStatus value, as follows

The first time a task object is constructed, his state is created. Later, when the task starts, his state becomes waitingtorun. When a task actually runs on a thread, his state becomes running. When the task stops running and waits for any of his subtasks, the state becomes waitingforchildrentocomplete. When the task completes, enter one of the states: Rantocompoletion (Run complete), Canceled (cancel), or faulted (error). If the run is complete, you can query the task results by task<tresult> the result property. On an error, you can query the exception property of the task to get the unhandled exception thrown by the job, which always returns a AggregateException object, and the InnerException collection of the object contains all the unhandled exceptions.

To simplify coding, the task provides several read-only Boolean properties, including Iscanceled,isfaulted and iscompleted.

The task object created by calling methods such as ContinueWith is in the waitingforactivation state. This state means that the dispatch of a task is controlled by the mission infrastructure and is automatically started.

2.3.2 Mission Factory

Sometimes you need to create a set of task objects that share the same configuration. To avoid mechanical assignment, we can create a task factory to encapsulate common configurations, TaskFactory and taskfactory<tresult>. To create a factory class, you need to pass the default values that you need to the constructor, such as CancellationToken, TaskScheduler, TaskCreationOptions, Taskcontinuationoptions, and so on.

The instance code is as follows

 Public Static voidGo () {Task parent=NewTask (() =    {        varCTS =NewCancellationTokenSource (); vartf =NewTaskfactory<int32>(CTS. Token, Taskcreationoptions.attachedtoparent, taskcontinuationoptions.executesynchronously, TaskScheduler.Default)        ; varChildtasks =New[] {tf. StartNew (()+ = Sum (cts. Token,10000) ), TF. StartNew (()+ = Sum (cts. Token,20000) ), TF. StartNew (()+ = Sum (cts. Token,int32.maxvalue))//execution here will throw a wrong        }; //any child task throws an exception, cancels the remaining subtasks         for(intTask =0; Task < Childtasks.length; task++) {Childtasks[task]. ContinueWith (t=CTS.        Cancel (), taskcontinuationoptions.onlyonfaulted); }        //when all subtasks are complete, the task that never fails/is canceled gets the maximum value returned,//then pass the maximum value to another task to display the maximum resultTf. Continuewhenall (childtasks, completedtasks = completedtasks.where (t =!t.isfaulted &&!t.iscanceled). Max (t = t.result), cancellationtoken.none). ContinueWith (t = Console.WriteLine ("The maximum is:"+t.result), taskcontinuationoptions.executesynchronously);    }); //Show any unhandled exceptions when the child task is completeParent. ContinueWith (p =    { //This first collects the input with StringBuilder and then calls once Console.WriteLine ()StringBuilder SB =NewStringBuilder ("The following exception (s) occurred:"+Environment.NewLine); foreach(varEinchP.exception.flatten (). innerexceptions) {sb. Append (" "+E.gettype ().        ToString ()); } Console.WriteLine (sb.)    ToString ()); }, taskcontinuationoptions.onlyonfaulted);p arent. Start ();p arent. Wait ();}Private StaticInt32 sum (CancellationToken ct, Int32 n) {Int32 sum=0;  for(; n>0; n--) {Ct.        Throwifcancellationrequested (); checked{sum+=N; }    }    returnsum;}
Task Factory Code

2.3.3 Task Scheduling

The task infrastructure is very flexible and the TaskScheduler object is a must. The TaskScheduler assigns the task's schedule and exposes the task information to the Visual Studio debugger. The FCL provides two types derived from TaskScheduler: The thread pool Task Scheduler (thread Pool Task Scheduler), and the synchronization Context Task Scheduler (Synchronization Context Task Scheduler). By default, the thread pool Task Scheduler is used. The synchronization Context Task Scheduler is suitable for applications that provide a graphical user interface, such as WPF,UWP.

Resources:

"CLR via C # (fourth edition)"

Msdn

Stephen Cleary related Async articles

First article, so first write the basics, and follow-up will discuss the practice of asynchronous programming in depth.

C # Asynchronous Programming (i) threading and asynchronous Programming fundamentals

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.