你对 .net 中的 Async/Await 和 thread了解多少?

作者:微信公众号:【架构师老卢】
2-19 18:29
26

概述:1. 引言在深入了解 Async/Await 的强大功能之前,必须对 .NET 运行时 (CLR) 及其基础组件有深入的了解。这涉及将 .NET 代码编译为 CLR,这有助于跨平台执行。为了建立这个基础,我们将探讨线程池、任务和任务计划程序等基本术语。这些知识对于编写高效且防错的代码至关重要。2. .NET 运行时 (CLR) 概述.NET 运行时提供有助于无缝执行应用程序的基本服务。主要功能包括:即时 (JIT) 编译: 在运行时将中间语言 (IL) 代码转换为本机机器代码。垃圾回收: 高效管理 JVM 内存。**线程管理:**通过线程池编排主应用程序线程和其他线程,例如为 Web 服务器中

1. 引言

在深入了解 Async/Await 的强大功能之前,必须对 .NET 运行时 (CLR) 及其基础组件有深入的了解。这涉及将 .NET 代码编译为 CLR,这有助于跨平台执行。为了建立这个基础,我们将探讨线程池、任务和任务计划程序等基本术语。这些知识对于编写高效且防错的代码至关重要。

2. .NET 运行时 (CLR) 概述

.NET 运行时提供有助于无缝执行应用程序的基本服务。主要功能包括:

  • 即时 (JIT) 编译: 在运行时将中间语言 (IL) 代码转换为本机机器代码。
  • 垃圾回收: 高效管理 JVM 内存。
  • **线程管理:**通过线程池编排主应用程序线程和其他线程,例如为 Web 服务器中的每个 API 调用创建的线程。
  • **装配加载和反射:**在运行时动态检查类型和调用方法。
  • **安全服务:**实施代码访问安全策略以规范代码权限。

3. .NET Runtime 中的线程管理

线程管理是 .NET 运行时中的一个关键方面,涉及各种组件(如线程计划程序、线程池、JIT 编译器、垃圾回收器和同步机制)的协调。这些元素协同工作,为执行多线程应用程序提供托管且高效的环境。

4. 了解线程池

若要理解线程管理,必须了解线程池,它是 .NET 运行时的基本组件。线程池管理器负责监督线程的创建和生命周期。线程池中存在两种不同类型的池:工作线程和完成端口线程

线程池 一些基本组件
1. 线程池管理器 =>管理线程的创建和线程的生命周期
2.线程池中有两种类型的池,一种是工作线程池**,一种是完成线程池**。
工作线程**:**
线程池中的主要实体是工作线程。
这些线程用于在应用程序中执行短期任务。
完成端口线程**:**
完成端口线程与异步 I/O 操作相关联,并与 I/O 完成端口机制结合使用。

了解它们的动态特性,包括它们的动态大小和开发人员可配置的限制,这一点至关重要。

ThreadPool.GetMinThreads(out int minWorkerThreads, out int minCompletionPortThreads);  
ThreadPool.GetMaxThreads(out int maxWorkerThreads, out int maxCompletionPortThreads);Console.WriteLine($"Min Worker Threads: {minWorkerThreads}");  
Console.WriteLine($"Max Worker Threads: {maxWorkerThreads}");  
Console.WriteLine($"Min Completion Port Threads: {minCompletionPortThreads}");  
Console.WriteLine($"Max Completion Port Threads: {maxCompletionPortThreads}");This is Result of my Local setup of .net Core Application  
Min Worker Threads: 8  
Max Worker Threads: 32767  
Min Completion Port Threads: 8  
Max Completion Port Threads: 1000

5. 任务队列和任务计划程序

线程池管理等待执行的任务队列。这些任务放置在任务队列中**,任务计划程序**负责有效地将这些任务调度到可用线程。

简洁
的任务队列:
线程池管理需要执行的任务队列。
将任务提交到线程池时,该任务将放置在任务队列中。
任务计划程序**:**
任务计划程序是负责将任务从队列计划到可用线程的组件。
它确定哪个工作线程或完成端口线程应执行特定任务。配置最小和最大池大小可进一步增强对线程分配的控制。

ThreadPool.SetMinThreads(100, 300);  
ThreadPool.SetMaxThreads(1000, 500);  
First argument in both functions is the number of worker threads and   
second is the number of Completion port threads.   
If current request equal to 1000, and developers want to create a new thread, that request   
remains in queue until the thread pool is available to allocate thread.  

6. 异步/等待机制

过渡到 Async/Await 领域时,必须了解 async 关键字在 C# 方法中的作用。异步方法表示它包含一个操作,其过程耗时,具体取决于外部因素。此关键字允许使用 await 并生成状态机来管理异步流,从而在不阻塞线程的情况下促进高效处理。

async 关键字告诉编译器以特殊方式处理该方法,允许它使用 await 关键字并生成状态机来处理异步流。
async 关键字主要用于表示包含异步操作的方法,允许使用 await 关键字并发出方法返回 Task 或 Task<T> 的信号。它不直接控制运行该方法的线程,但它支持异步编程模式,从而在不阻塞线程的情况下高效处理异步操作。它允许该方法在异步等待期间将控制权交还给调用方,从而避免线程阻塞并促进更好的可伸缩性和响应能力。

async Task MyAsyncMethod()  
{  
    // Asynchronous operations with await  
}

7. 异步的力量

async 关键字使开发人员能够创建具有异步操作的方法,利用 await 关键字来促进非阻塞执行。此方法允许方法在异步等待期间产生控制权,从而防止线程阻塞,从而增强了可伸缩性、响应能力和整体代码效率。

通过全面了解这些基本概念,开发人员可以利用 Async/Await 的全部功能进行稳健高效的代码开发。

现在来举一些例子

  1. 场景:基本异步/等待流
using System;
using System.Threading.Tasks;
using Newtonsoft.Json;

class Program
{
    static async void asyncTask()
    {
        Console.WriteLine($"Before await thread is working on it CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)} ");
        await Task.Delay(10000);
        Console.WriteLine($"After await thread is working on it CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)}");
    }
    static void Main(string[] args)
    {

        Console.WriteLine($"Before asyncTask() method called the Main Thread is running CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)}");
        asyncTask();
        Console.WriteLine($"After asyncTask() Thread is running CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)}");
    }
}
Before asyncTask() method called the Main Thread is running CurrentThreadInfo: {"ManagedThreadId":1,"IsAlive":true,"IsBackground":false,"IsThreadPoolThread":false,"Priority":2,"ThreadState":0,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":null,"ApartmentState":1}
Before await thread is working on it CurrentThreadInfo: {"ManagedThreadId":1,"IsAlive":true,"IsBackground":false,"IsThreadPoolThread":false,"Priority":2,"ThreadState":0,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":null,"ApartmentState":1}
After asyncTask() Thread is running CurrentThreadInfo: {"ManagedThreadId":1,"IsAlive":true,"IsBackground":false,"IsThreadPoolThread":false,"Priority":2,"ThreadState":0,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":null,"ApartmentState":1}

After await thread is working on it CurrentThreadInfo: {"ManagedThreadId":8,"IsAlive":true,"IsBackground":true,"IsThreadPoolThread":true,"Priority":2,"ThreadState":4,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":".NET ThreadPool Worker","ApartmentState":1}

在此方案中,我们有一个简单的异步方法,使用 和 with 。主要观察结果如下:asyncTask()asyncawaitTask.Delay(10000)

  • 主螺纹识别: 主线程(由其托管 ThreadId 1 标识)在调用 之前和之后运行。整个过程发生在同一个线程上。它允许该方法在异步等待期间将控制权交还给调用方。asyncTask()
  • 异步操作线程:在异步延迟期间,线程池中的新线程(托管 ThreadId:8)将接管。这证明了 的非阻塞性质,允许主线程继续执行。async/await

此方案展示了异步操作期间线程之间的无缝转换,突出了应用程序的响应能力。

2. 场景:同步异步/等待

using System;  
using Newtonsoft.Json;  
  
class Program  
{  
    static async void asyncTask()  
    {  
        Console.WriteLine($"Before await thread is working on it CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)} ");  
        Console.WriteLine($"After await thread is working on it CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)}");  
    }  
    static void Main(string[] args)  
    {  
  
        Console.WriteLine($"Before asyncTask() method called the Main Thread is running CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)}");  
        asyncTask();  
        Console.WriteLine($"After asyncTask() Thread is running CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)}");  
    }  
}
Before asyncTask() method called the Main Thread is running CurrentThreadInfo: {"ManagedThreadId":1,"IsAlive":true,"IsBackground":false,"IsThreadPoolThread":false,"Priority":2,"ThreadState":0,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":null,"ApartmentState":1}  
Before await thread is working on it CurrentThreadInfo: {"ManagedThreadId":1,"IsAlive":true,"IsBackground":false,"IsThreadPoolThread":false,"Priority":2,"ThreadState":0,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":null,"ApartmentState":1}  
After await thread is working on it CurrentThreadInfo: {"ManagedThreadId":1,"IsAlive":true,"IsBackground":false,"IsThreadPoolThread":false,"Priority":2,"ThreadState":0,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":null,"ApartmentState":1}  
After asyncTask() Thread is running CurrentThreadInfo: {"ManagedThreadId":1,"IsAlive":true,"IsBackground":false,"IsThreadPoolThread":false,"Priority":2,"ThreadState":0,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":null,"ApartmentState":1}

这里,仅包含同步操作,揭示了一个有趣的行为:asyncTask()

  • 主线程连续性:主线程 (ManagedThreadId: 1) 在调用之前、期间和之后保持一致。不会引入 ThreadPool 中的其他线程。asyncTask()

此方案强调,并非所有标记为的方法都涉及线程切换,并且没有异步操作会导致在调用线程上直接同步执行。async

3. 场景:Async 方法中缺少 Await

using System;  
using System.Threading.Tasks;  
using Newtonsoft.Json;  
  
class Program  
{  
     static async void asyncTask()  
    {  
        Console.WriteLine($"Before await thread is working on it CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)} ");  
        Task.Delay(10000);  
        Console.WriteLine($"After await thread is working on it CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)}");  
    }  
    static void Main(string[] args)  
    {  
  
        Console.WriteLine($"Before asyncTask() method called the Main Thread is running CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)}");  
        asyncTask();  
        Console.WriteLine($"After asyncTask() Thread is running CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)}");  
    }  
}
Before asyncTask() method called the Main Thread is running CurrentThreadInfo: {"ManagedThreadId":1,"IsAlive":true,"IsBackground":false,"IsThreadPoolThread":false,"Priority":2,"ThreadState":0,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":null,"ApartmentState":1}  
Before await thread is working on it CurrentThreadInfo: {"ManagedThreadId":1,"IsAlive":true,"IsBackground":false,"IsThreadPoolThread":false,"Priority":2,"ThreadState":0,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":null,"ApartmentState":1}  
After await thread is working on it CurrentThreadInfo: {"ManagedThreadId":1,"IsAlive":true,"IsBackground":false,"IsThreadPoolThread":false,"Priority":2,"ThreadState":0,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":null,"ApartmentState":1}  
After asyncTask() Thread is running CurrentThreadInfo: {"ManagedThreadId":1,"IsAlive":true,"IsBackground":false,"IsThreadPoolThread":false,"Priority":2,"ThreadState":0,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":null,"ApartmentState":1}

在这种情况下,缺少 中的关键字。观察结果如下:Task.Delay(10000)awaitasyncTask()

  • **主线程一致性:主线程(托管 ThreadId:1)**在整个进程中持续存在,包括之前和之后。asyncTask()
  • **异步操作被忽略:尽管该方法被标记为 ,但缺少会导致异步操作被忽略,从而导致同步行为。这里线程 ID 1 等待 10000 毫秒,然后打印“After await thread is working”。**asyncawait

此方案强调了正确利用以实现所需的异步行为的重要性。await

4. 场景:使用 Thread.Sleep 自定义线程

using System;  
using System.Threading;  
using Newtonsoft.Json;  
  
class Program  
{  
    static async void asyncTask()  
    {  
        Console.WriteLine($"Before await thread is working on it CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)} ");  
        Thread.Sleep(10000);  
        Console.WriteLine($"After await thread is working on it CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)}");  
    }  
    static void Main(string[] args)  
    {  
  
        Console.WriteLine($"Before asyncTask() method called the Main Thread is running CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)}");  
        Thread customThread = new Thread(asyncTask);  
        customThread.IsBackground = true;  
        customThread.Start();  
        Console.WriteLine($"After asyncTask() Thread is running CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)}");    
    }  
}
Before asyncTask() method called the Main Thread is running CurrentThreadInfo: {"ManagedThreadId":1,"IsAlive":true,"IsBackground":false,"IsThreadPoolThread":false,"Priority":2,"ThreadState":0,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":null,"ApartmentState":1}  
After asyncTask() Thread is running CurrentThreadInfo: {"ManagedThreadId":1,"IsAlive":true,"IsBackground":false,"IsThreadPoolThread":false,"Priority":2,"ThreadState":0,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":null,"ApartmentState":1}  
Before await thread is working on it CurrentThreadInfo: {"ManagedThreadId":10,"IsAlive":true,"IsBackground":true,"IsThreadPoolThread":false,"Priority":2,"ThreadState":4,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":null,"ApartmentState":1}  
After await thread is working on it CurrentThreadInfo: {"ManagedThreadId":10,"IsAlive":true,"IsBackground":true,"IsThreadPoolThread":false,"Priority":2,"ThreadState":4,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":null,"ApartmentState":1}

在这里,使用 to execute 创建了一个自定义线程,并引入了一个:new Thread()asyncTask()Thread.Sleep(1000)

  • **主线程独立性:**主线程 (ManagedThreadId: 1) 不受自定义线程的影响,允许它独立进行。
  • **自定义线程开销:**创建自定义线程会带来开销,这在启动自定义线程与其实际执行之间的延迟中可见一斑。

此方案说明了与自定义线程创建相关的其他复杂性,强调了控制和开销之间的权衡。

5. 场景:具有主线程交互的自定义线程

using System;  
using System.Threading;  
using Newtonsoft.Json;  
  
class Program  
{  
    static async void asyncTask()  
    {  
        Console.WriteLine($"Before await thread is working on it CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)} ");  
        Thread.Sleep(10000);  
        Console.WriteLine($"After await thread is working on it CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)}");  
    }  
    static void Main(string[] args)  
    {  
  
        Console.WriteLine($"Before asyncTask() method called the Main Thread is running CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)}");  
        Thread customThread = new Thread(asyncTask);  
        customThread.Start();  
        Thread.Sleep(1000);  
        Console.WriteLine($"After asyncTask() Thread is running CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)}");  
    }  
}
Before asyncTask() method called the Main Thread is running CurrentThreadInfo: {"ManagedThreadId":1,"IsAlive":true,"IsBackground":false,"IsThreadPoolThread":false,"Priority":2,"ThreadState":0,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":null,"ApartmentState":1}  
Before await thread is working on it CurrentThreadInfo: {"ManagedThreadId":12,"IsAlive":true,"IsBackground":false,"IsThreadPoolThread":false,"Priority":2,"ThreadState":0,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":null,"ApartmentState":1}  
After asyncTask() Thread is running CurrentThreadInfo: {"ManagedThreadId":1,"IsAlive":true,"IsBackground":false,"IsThreadPoolThread":false,"Priority":2,"ThreadState":0,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":null,"ApartmentState":1}  
After await thread is working on it CurrentThreadInfo: {"ManagedThreadId":12,"IsAlive":true,"IsBackground":false,"IsThreadPoolThread":false,"Priority":2,"ThreadState":0,"CurrentCulture":"en-IN","CurrentUICulture":"en-US","ExecutionContext":{},"Name":null,"ApartmentState":1}

在最后一种情况下,自定义线程在主线程短暂休眠时运行:asyncTask()

  • **主线程交互:**主线程 (ManagedThreadId: 1) 和自定义线程 (ManagedThreadId: 12) 并发运行,演示并行性。
  • **异步操作执行:**尽管主线程处于休眠状态,但自定义线程仍会执行异步操作,从而显示操作的异步性质。

此方案突出了并行执行的潜力,以及了解并发环境中多个线程之间的相互作用的重要性。

总之,这些方案提供了对各种异步方案中线程行为的见解,帮助开发人员了解 C# 应用程序中多线程的复杂性。

相关留言评论
昵称:
邮箱:
阅读排行