Async/Await与线程:.NET Core C#7.2中await正确用法咨询
Alright, let's break this down clearly—first the core rules for using await correctly in C#, then we'll run through your async/await threading code and explain exactly what's happening under the hood.
Key Rules for Correctly Using
await in C# - Only use
awaitinside async methods: You can’t useawaitin a non-async method (unless you resort to.GetAwaiter().GetResult(), but this is risky and often causes deadlocks—avoid it unless you have no other choice). - Mark methods with
asyncwhen usingawait: Any method containing anawaitoperator should be tagged with theasyncmodifier. Keep in mind:asyncis "contagious"—if you call an async method, your calling method should usually be async too. - Return the right type: Async methods should return
Task(for void-like operations),Task<T>(for operations that return a value), orValueTask<T>(for high-performance scenarios where results might be available synchronously). Avoidasync voidexcept for event handlers—it makes error handling and completion tracking nearly impossible. - Never block on async code: Using
.Wait()or.Result()on aTaskcan trigger deadlocks, especially in UI contexts or ASP.NET. Always useawaitinstead to let the runtime handle context switching properly. - Use
Task.WhenAll/Task.WhenAnyfor parallelism: If you have independent tasks, don’t await them one after another if you want concurrency. Start all tasks first, then useTask.WhenAllto await their combined completion (this cuts total execution time significantly). - Optimize context capture with
ConfigureAwait(false): By default,awaitcaptures the current synchronization context (like a UI thread) and resumes execution there. If you don’t need this (e.g., in library code), useawait task.ConfigureAwait(false)to skip context switching and boost performance.
Running & Analyzing Your .NET Core Async/Await Threading Code
First, let’s fill in the missing Watch and NotParallel methods to make your code runnable (these are logical implementations that match your original intent):
using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; static class Program { static async Task<string> GetTaskAsync(int timeout) { Console.WriteLine($"Task Thread (start): {Thread.CurrentThread.ManagedThreadId}"); await Task.Delay(timeout); Console.WriteLine($"Task Thread (resume): {Thread.CurrentThread.ManagedThreadId}"); return timeout.ToString(); } static async Task NotParallel() { // Run async tasks sequentially await GetTaskAsync(2000); await GetTaskAsync(3000); } static async Task Watch(Func<Task> action) { var stopwatch = Stopwatch.StartNew(); await action(); stopwatch.Stop(); Console.WriteLine($"Elapsed time: {stopwatch.ElapsedMilliseconds}ms"); } static async Task Main() { Console.WriteLine($"Main Thread (start): {Thread.CurrentThread.ManagedThreadId}"); Console.WriteLine("Should be greater than 5000"); await Watch(NotParallel); Console.WriteLine("Sho..."); } }
Sample Output (on .NET 6+)
Main Thread (start): 1 Should be greater than 5000 Task Thread (start): 1 Task Thread (resume): 4 Task Thread (start): 4 Task Thread (resume): 4 Elapsed time: 5002ms Sho...
What’s Happening with Threads?
- Initial synchronous execution: The
Mainmethod starts on thread 1. When it callsGetTaskAsync(2000), the first part of that method runs synchronously on the same thread (hence the first "Task Thread (start): 1" line). - Yielding control with
await: When we hitawait Task.Delay(2000), the method yields control back up the call stack (toNotParallel, thenWatch, thenMain). The thread pool handles the delay, and when it finishes, the continuation (code afterawait) runs on a thread pool thread (thread 4 here). - Sequential task execution:
NotParallelwaits for the first task to finish before starting the second. This means total execution time is ~2000ms + 3000ms = 5000ms, which matches the elapsed time in the output. - Thread resumption: Console apps don’t have a synchronization context, so
awaitresumes execution on any available thread pool thread. That’s why the second task starts and resumes on thread 4 instead of going back to thread 1.
For Parallel Execution (If You Wanted It)
To run the two tasks concurrently and cut total time to ~3000ms, start both tasks first then await them together:
static async Task Parallel() { var task1 = GetTaskAsync(2000); var task2 = GetTaskAsync(3000); await Task.WhenAll(task1, task2); }
内容的提问来源于stack exchange,提问作者alerya
相关产品推荐
相关产品推荐

