在ASP.NET Core中调用异步方法而不等待的问题。

huangapple go评论70阅读模式
英文:

Issue about calling an async method without awaiting in ASP.NET Core

问题

ASP.NET Core Web API控制器中,我们有一个要求:应该在后台创建和执行作业,并且一旦作业开始运行,控制器应立即回复响应。将会有另一个控制器API可以查询作业的执行状态,但那是一个不同的话题。

简单来说,代码类似于以下内容:

```csharp
public class Job
{
  public async Task RunAsync()
  {
    //...
  }
}

// API控制器代码
private Job _job;

[HttpPost]
public async Task<IActionResult> CreateJob()
{
    _job.RunAsync(); // <-- 没有等待,使作业在后台运行
    await DoAnotherThingAsync();
    return Ok();
}

我发现有时这个后台作业的运行会因某些原因而中断 - 在CreateJob API返回后,似乎_job.RunAsync()并不总是完全完成其工作 - 它的执行线程随时可能被终止。

我知道这不是一个好的实现,但有一些问题:

  1. 为什么会中断这个后台作业的执行?
  2. 如果我在_job.RunAsync()之前添加await,这个CreateJob API最终将等待后台作业完成,然后再返回给调用者,这会使作业执行变成同步的,对吗?
  3. 除了采用像Quartz.NET和Hangfire这样的第三方框架,还有没有解决这个问题的简单方法?

<details>
<summary>英文:</summary>

Here we have a requirement that in the ASP.NET Core Web API controller, a job should be created and executed in the background, and the controller should reply the response immediately once the job starts running. There will be another controller API which could query the execution status of the job but that is a different topic.

To be simpler, the code is similar to the following:

```csharp
public class Job
{
  public async Task RunAsync()
  {
    //...
  }
}

// API Controller code
private Job _job;

[HttpPost]
public async Task&lt;IActionResult&gt; CreateJob()
{
    _job.RunAsync(); // &lt;-- No awaiting, making the job running in the background
    await DoAnotherThingAsync();
    return Ok();
}

I found that sometimes the running of this background job will be interrupted for some reasons - After this CreateJob API has returned, it looks like the _job.RunAsync() will not always completely finish its work - its execution thread could be killed at any time.

I know this is not a good implementation, but there are some questions:

  1. Why could this background job execution be interrupted?
  2. If I append await before _job.RunAsync(), this CreateJob API will eventually wait the background job to complete before it returns to the caller, which makes the job execution to be synchronous, right?
  3. Except adopting third party frameworks like Quartz.NET and Hangfire, is there any simple solution to this issue?

答案1

得分: 2

I found that sometimes the running of this background job will be interrupted for some reasons - After this CreateJob API has returned, it looks like the _job.RunAsync() will not always completely finish its work - its execution thread could be killed at any time.

Yup.

Why could this background job execution be interrupted?

Any number of reasons! I explain several of them in my blog post (see the section "Shutdowns Are Normal"):

  • Rolling updates.
  • Applying OS or runtime patches.
  • Periodic restarts built-in to ASP.NET/IIS.

In-memory work cannot survive application shutdown (and shutdowns are normal), so the only proper (reliable) solution is to store them somewhere durable (i.e., not in memory).

If I append await before _job.RunAsync(), this CreateJob API will eventually wait the background job to complete before it returns to the caller, which makes the job execution to be synchronous, right?

Well, that depends on what you mean by "synchronous".

I would say that no, it's still asynchronous. With the await, the request thread is not blocked, so it's asynchronous.

But: ASP.NET will (asynchronously) wait until the job completes before returning the HTTP response.

Since ASP.NET doesn't return the HTTP response until the job is completed, then at a higher conceptual level you could say it was "synchronous".

Except adopting third party frameworks like Quartz.NET and Hangfire, is there any simple solution to this issue?

No.

I have a blog series on this subject explaining why it's complex. Hangfire is about as simple of a solution as you're going to get.

英文:

> I found that sometimes the running of this background job will be interrupted for some reasons - After this CreateJob API has returned, it looks like the _job.RunAsync() will not always completely finish its work - its execution thread could be killed at any time.

Yup.

> Why could this background job execution be interrupted?

Any number of reasons! I explain several of them in my blog post (see the section "Shutdowns Are Normal"):

  • Rolling updates.
  • Applying OS or runtime patches.
  • Periodic restarts built-in to ASP.NET/IIS.

In-memory work cannot survive application shutdown (and shutdowns are normal), so the only proper (reliable) solution is to store them somewhere durable (i.e., not in memory).

> If I append await before _job.RunAsync(), this CreateJob API will eventually wait the background job to complete before it returns to the caller, which makes the job execution to be synchronous, right?

Well, that depends on what you mean by "synchronous".

I would say that no, it's still asynchronous. With the await, the request thread is not blocked, so it's asynchronous.

But: ASP.NET will (asynchronously) wait until the job completes before returning the HTTP response.

Since ASP.NET doesn't return the HTTP response until the job is completed, then at a higher conceptual level you could say it was "synchronous".

> Except adopting third party frameworks like Quartz.NET and Hangfire, is there any simple solution to this issue?

No.

I have a blog series on this subject explaining why it's complex. Hangfire is about as simple of a solution as you're going to get.

答案2

得分: 0

第一个任务被中断,因为该方法没有监控它。不管第一个任务的状态如何,该方法都会在第二个任务完成时返回。您必须使用同步来确保在离开该方法之前两个任务都已完成。

var task1 = DoWorkAsync();
var task2 = DoMoreWorkAsync();

await Task.WhenAll(task1, task2);
英文:

The first job interrupted because the method doesn't monitor it. The method returns exactly when second job finishes regardless the state of the first. You have to use synchronisation to ensure both jobs finished before leaving the method

var task1 = DoWorkAsync();
var task2 = DoMoreWorkAsync();

await Task.WhenAll(task1, task2);

huangapple
  • 本文由 发表于 2023年6月13日 15:48:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76462728.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定