英文:
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()
并不总是完全完成其工作 - 它的执行线程随时可能被终止。
我知道这不是一个好的实现,但有一些问题:
- 为什么会中断这个后台作业的执行?
- 如果我在
_job.RunAsync()
之前添加await
,这个CreateJob
API最终将等待后台作业完成,然后再返回给调用者,这会使作业执行变成同步的,对吗? - 除了采用像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<IActionResult> CreateJob()
{
_job.RunAsync(); // <-- 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:
- Why could this background job execution be interrupted?
- If I append
await
before_job.RunAsync()
, thisCreateJob
API will eventually wait the background job to complete before it returns to the caller, which makes the job execution to be synchronous, right? - 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);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论