Skip to content

C# 多线程 05-使用 C#6.0 07-使用 async void 方法

🏷️ 《C# 多线程》

07-使用 async void 方法

csharp
/// <summary>
/// 使用 async void 方法
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
    // 使用 async Task 可以通过返回值的 Task 示例,监控任务的状态
    Task t = AsyncTask();
    t.Wait();

    // 使用 async void 没有返回值,无法监控任务状态
    AsyncVoid();
    // 这里使用 Sleep 方法确保任务完成
    Thread.Sleep(TimeSpan.FromSeconds(3));

    // 根据 Task.IsFaulted 属性可以判断是否发生异常
    // 捕获的异常信息可以从 Task.Exception 中获取
    t = AsyncTaskWithErrors();
    while (!t.IsFaulted)
    {
        Thread.Sleep(TimeSpan.FromSeconds(1));
    }
    Console.WriteLine(t.Exception);

    // 该段代码虽然使用 try/catch 捕获异常,但是由于使用了 async void 方法,
    // 异常处理方法会被放置到当前的同步上下文中(即线程池的线程中)。
    // 线程池中未被处理的异常会终止整个进程。

    //try
    //{
    //    AsyncVoidWithErrors();
    //    Thread.Sleep(TimeSpan.FromSeconds(3));
    //}
    //catch (Exception ex)
    //{
    //    Console.WriteLine(ex);
    //}

    // Action 类型也是可以使用 async 关键字的;
    // 在 lambda 表达式中很容易忘记对异常的处理,而这会导致程序崩溃。
    int[] numbers = { 1, 2, 3, 4, 5 };
    Array.ForEach(numbers, async number =>
    {
        await Task.Delay(TimeSpan.FromSeconds(1));
        if (number == 3)
        {
            throw new Exception("Boom!");
        }
        Console.WriteLine(number);
    });

    Console.ReadLine();
}

static async Task AsyncTaskWithErrors()
{
    string result = await GetInfoAsync("AsyncTaskException", 2);
    Console.WriteLine(result);
}

static async void AsyncVoidWithErrors()
{
    string result = await GetInfoAsync("AsyncVoidException", 2);
    Console.WriteLine(result);
}

static async Task AsyncTask()
{
    string result = await GetInfoAsync("AsyncTask", 2);
    Console.WriteLine(result);
}

static async void AsyncVoid()
{
    string result = await GetInfoAsync("AsyncVoid", 2);
    Console.WriteLine(result);
}

static async Task<string> GetInfoAsync(string name, int seconds)
{
    await Task.Delay(TimeSpan.FromSeconds(seconds));
    if (name.Contains("Exception"))
    {
        throw new Exception($"Boom from {name}");
    }
    return $"Task {name} is running on a thrad id {Thread.CurrentThread.ManagedThreadId}. " +
        $"Is thread pool thread: {Thread.CurrentThread.IsThreadPoolThread}";
}

运行结果

txt
Task AsyncTask is running on a thrad id 4. Is thread pool thread: True
Task AsyncVoid is running on a thrad id 4. Is thread pool thread: True
System.AggregateException: 发生一个或多个错误。 ---> System.Exception: Boom from
 AsyncTaskException
   在 Recipe5_7.Program.<GetInfoAsync>d__5.MoveNext() 位置 C:\Users\liujiajia\So
urce\Repos\learn-multi-threading\Recipe5-7\Program.cs:行号 88
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
ification(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   在 Recipe5_7.Program.<AsyncTaskWithErrors>d__1.MoveNext() 位置 C:\Users\liuji
ajia\Source\Repos\learn-multi-threading\Recipe5-7\Program.cs:行号 61
   --- 内部异常堆栈跟踪的结尾 ---
---> (内部异常 #0) System.Exception: Boom from AsyncTaskException
   在 Recipe5_7.Program.<GetInfoAsync>d__5.MoveNext() 位置 C:\Users\liujiajia\So
urce\Repos\learn-multi-threading\Recipe5-7\Program.cs:行号 88
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
ification(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   在 Recipe5_7.Program.<AsyncTaskWithErrors>d__1.MoveNext() 位置 C:\Users\liuji
ajia\Source\Repos\learn-multi-threading\Recipe5-7\Program.cs:行号 61<---

5
2
1
4

未经处理的异常:  System.Exception: Boom!
   在 Recipe5_7.Program.<>c.<<Main>b__0_0>d.MoveNext() 位置 C:\Users\liujiajia\S
ource\Repos\learn-multi-threading\Recipe5-7\Program.cs:行号 51
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
   在 System.Runtime.CompilerServices.AsyncMethodBuilderCore.<>c.<ThrowAsync>b__
6_1(Object state)
   在 System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object sta
te)
   在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionCo
ntext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, C
ontextCallback callback, Object state, Boolean preserveSyncCtx)
   在 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWor
kItem.ExecuteWorkItem()
   在 System.Threading.ThreadPoolWorkQueue.Dispatch()
   在 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

放开 main 方法中间的备注代码,会发生如下异常:

未经处理的异常:  System.Exception: Boom from AsyncVoidException
   在 Recipe5_7.Program.<GetInfoAsync>d__5.MoveNext() 位置 C:\Users\liujiajia\So
urce\Repos\learn-multi-threading\Recipe5-7\Program.cs:行号 89
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
ification(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   在 Recipe5_7.Program.<AsyncVoidWithErrors>d__2.MoveNext() 位置 C:\Users\liuji
ajia\Source\Repos\learn-multi-threading\Recipe5-7\Program.cs:行号 68
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
   在 System.Runtime.CompilerServices.AsyncMethodBuilderCore.<>c.<ThrowAsync>b__
6_1(Object state)
   在 System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object sta
te)
   在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionCo
ntext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, C
ontextCallback callback, Object state, Boolean preserveSyncCtx)
   在 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWor
kItem.ExecuteWorkItem()
   在 System.Threading.ThreadPoolWorkQueue.Dispatch()
   在 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

TIP

强烈建议只在 UI 事件处理器中使用 async void 方法。在其它所有的情况下,请使用返回 Task 的方法。