乐趣区

关于c#:async-void-导致程序崩溃

起源:https://note.guoqianfan.com/2…

前言

之前都是在文档里看到:除了 winform 的事件 能够应用 async void,其余状况下 相对不能 应用async void,而是要用async Task

对于这个标准,尽管不是很明确内里起因,然而始终恪守着。

直到这天看到了这篇博客:在 ASP.NET Core 中誤用 async void 竟引發了 502(Bad Gateway),说 async void 里出现异常时会导致程序解体。钻研测试了一番,终于明确起因。

摘录重点如下:

根據使用者提供的另一個線索「網站的某個性能壞了」,我們繼續往下追究,從程式碼當中我看到了一個近期新加的办法,它应用了 async void,沒錯,它应用了 async void,而且很可怜地它會發生 Exception,更慘的是這個 Exception 沒有被處理。

對 C# 非同步程式設計有理解的敌人,看到這邊應該大抵上能够晓得是發什麼問題了,async void 是建議應該防止应用的宣告形式,其中一個起因就是當 async void 办法發生 Exception 時無法從呼叫端捕獲,即便加了 try…catch… 也沒用,async void 办法就有點像是我們本人起了另一個 Thread 去執行程式一樣,執行的過程中如果發生 Exception 沒有去處理,Exception 就會一路被往上拋,最終在 AppDomain 層級被捕獲,然後我們的應用程式就掛了。

async-void- 办法的异样无奈被捕捉

async void办法抛出的异样 无奈被捕捉,异样会被始终往上面抛,最终在 AppDomain 层级被捕捉,而后程序就挂了。

示例代码如下:

[HttpGet]
public async void Get()
{
    try
    {ThrowExceptionAsync();
    }
    catch (Exception ex)
    {
        // 这里不能捕捉到异样,程序解体!_logger.LogInformation(ex, "ex...");
    }
}

async void ThrowExceptionAsync()
{throw new Exception("async void ex!");
}

留神

后面所说的是 async void办法抛出的无奈预知到的异样 。在async void 办法外部,咱们依然可能应用try catch,逻辑是失常逻辑。具体见上面的示例:

[HttpGet]
public async void Get()
{
    // 在 async void 办法外部,咱们依然可能应用 try catch,逻辑是失常逻辑。// 此处 try catch 是无效的。异样被捕捉解决了,async void 办法执行无异样,不会导致程序解体。try
    {await Task.Run(() =>
        {throw new Exception("ex!");
        });
    }
    catch (Exception ex)
    {_logger.LogInformation(ex,"ex...");
    }
}

测试

解体

出现异常时能导致解体的代码有 2 种,如下:

[HttpGet]
public async void Get()
{
    // 异样会导致程序解体
    throw new Exception("ex!");
}

[HttpGet]
public async void Get()
{
    // 异样会导致程序解体
    await Task.Run(() =>
    {throw new Exception("ex!");
    });
}

留神

上面的 async void 代码不会抛异样。

[HttpGet]
public async void Get()
{Task.Run(() =>
    {throw new Exception("ex!");
    });
}

代码里的 async void 没问题 (不抛异样),其实也合乎逻辑。因为async void 外面 没有异样,天然就不会导致程序解体。

异样在 Task.Run 外面,因为 没有应用 await 进行期待 ,那么异样就是被 线程池线程 捕捉的,它们捕捉到后,不会再往上面抛了,间接本人外部消化掉了。

因为 async void 在执行时 没有异样,天然就不会导致程序解体。

然而因为咱们不能保障所有代码都没有异样,所以 不要 应用async void

不解体

只有 不是 async void,就算申请处理程序抛出了异样,也不会影响到 主线程 的。最多就是这次申请出错,返回 500 Internal Server Error 而已。

测试的几种代码如下:

[HttpGet]
public async Task Get()
{
    //500 错误码
    throw new Exception("ex!");
}

[HttpGet]
public async Task Get()
{
    //500 错误码
    await Task.Run(() =>
    {throw new Exception("ex!");
    });
}
退出移动版