當前位置: 妍妍網 > 碼農

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

2024-03-23碼農


概述: 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> 的訊號。它不直接控制執行該方法的執行緒,但它支持異步編程模式,從而在不阻塞執行緒的情況下高效處理異步操作。它允許該方法在異步等待期間將控制權交還給呼叫方,從而避免執行緒阻塞並促進更好的可伸縮性和響應能力。

    asyncTaskMyAsyncMethod()
    {
    // Asynchronous operations with await
    }

    7. 異步的力量

    async 關鍵字使開發人員能夠建立具有異步操作的方法,利用 await 關鍵字來促進非阻塞執行。此方法允許方法在異步等待期間產生控制權,從而防止執行緒阻塞,從而增強了可伸縮性、響應能力和整體程式碼效率。

    透過全面了解這些基本概念,開發人員可以利用 Async/Await 的全部功能進行穩健高效的程式碼開發。

    現在來舉一些例子

    1. 場景:基本異步/等待流

    usingSystem;
    usingSystem.Threading.Tasks;
    usingNewtonsoft.Json;
    classProgram
    {
    staticasyncvoidasyncTask()
    {
    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)}");
    }
    staticvoidMain(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. 場景:同步異步/等待

    usingSystem;
    usingNewtonsoft.Json;
    classProgram
    {
    staticasyncvoidasyncTask()
    {
    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)}");
    }
    staticvoidMain(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

    usingSystem;
    usingSystem.Threading.Tasks;
    usingNewtonsoft.Json;
    classProgram
    {
    staticasyncvoidasyncTask()
    {
    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)}");
    }
    staticvoidMain(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 自訂執行緒

    usingSystem;
    usingSystem.Threading;
    usingNewtonsoft.Json;
    classProgram
    {
    staticasyncvoidasyncTask()
    {
    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)}");
    }
    staticvoidMain(string[] args)
    {
    Console.WriteLine($"Before asyncTask() method called the Main Thread is running CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)}");
    Thread customThread = newThread(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. 場景:具有主執行緒互動的自訂執行緒

    usingSystem;
    usingSystem.Threading;
    usingNewtonsoft.Json;
    classProgram
    {
    staticasyncvoidasyncTask()
    {
    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)}");
    }
    staticvoidMain(string[] args)
    {
    Console.WriteLine($"Before asyncTask() method called the Main Thread is running CurrentThreadInfo: {JsonConvert.SerializeObject(Thread.CurrentThread)}");
    Thread customThread = newThread(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# 應用程式中多執行緒的復雜性。

    如果你喜歡我的文章,請給我一個贊!謝謝