当我们不等待异步方法时会发生什么



我有一个.NET CORE 2后端。在我的一个控制器端点中,我正在创建通过电子邮件发送的邀请。这似乎是端点上的一个巨大瓶颈,经过思考,我真的不需要等待这些邀请。如果电子邮件发不出去,我真的无能为力。

如果我不做await sendFn(),它本质上会是一种火和遗忘的方法吗?我在另一个stackoverflow线程上读到,我必须执行sendFn().ContinueWith(t => throw(t))才能捕获异常,因为它将在另一线程中。

我在代码库中有类似的邮寄功能。它们各自做的事情略有不同,但我能做什么服务吗?fn把它们包起来,让它们燃烧起来,然后忘记?我认为有些地方我可以不使用await(如果有效的话(,但有些事情会改变数据库上下文,所以如果我不等待它们,我可能会遇到某个东西正在访问同一个数据库上下文的情况。

[HttpPost]
public async Task<IActionResult> CreateEvent([FromBody] Event val)
{
_ctx.Event.Add(val);
await _ctx.SaveChangesAsync();
await SendInvitations(val); // fn in question
return Ok();
}
public async Task SendInvitation(Event event)
{
forEach (var person in event.people)
{
await _ctx.Invitation.Add(person); // This shouldn't happen while another iteration or some other async code elsewhere is using the db ctx.
_ctx.SaveChangesAsync();
await _mailService.SendMail(person.email,"you have been invited"); // don't really need to await this.
}
}

我正在将有关某个事件的数据发布到我的服务器上。创建活动并将其保存到数据库后,我会为每个人创建邀请。这些邀请也是数据库项目。然后我发了一封电子邮件。我最担心的是,如果我放弃等待,那么当我创建邀请时,它可能会与其他地方或下一次迭代的数据库上下文冲突。

为了让代码编译并运行,我必须进行以下更改:

public async Task<IActionResult> CreateEvent(Event val)
{
_ctx.Event.Add(val);
await _ctx.SaveChangesAsync();
await SendInvitation(val);
return Ok();
}
public async Task SendInvitation(Event @event)
{
foreach (var person in @event.people)
{
await _ctx.Invitation.Add(person);
await _ctx.SaveChangesAsync();
await _mailService.SendMail(person.email, "you have been invited");
}
}

我还必须写下这个线束代码:

public OK Ok() => new OK();
public class Event
{
public List<Person> people = new List<Person>();
}
public class Person
{
public string email;
}
public interface IActionResult { }
public class OK : IActionResult { }
public class Invitation
{
public Task Add(Person person) => Task.Run(() => { });
}
public static class _ctx
{
public static List<Event> Event = new List<Event>();
public static Invitation Invitation = new Invitation();
public static Task SaveChangesAsync() { return Task.Run(() => { }); }
}
public static class _mailService
{
public static Task SendMail(string email, string message) { return Task.Run(() => { }); }
}

然后我更新了SendInvitation,如下所示:

public async Task SendInvitation(Event @event)
{
Thread.Sleep(2000);
foreach (var person in @event.people)
{
await _ctx.Invitation.Add(person);
await _ctx.SaveChangesAsync();
await _mailService.SendMail(person.email, "you have been invited");
}
Console.WriteLine("Done `SendInvitation`.");
}

现在,我可以这样运行:

var e = new Event();
e.people.Add(new Person() { email = "foo@bar.com" });
CreateEvent(e).ContinueWith(t => Console.WriteLine("Done `CreateEvent`."));
Console.WriteLine("Done `Main`.");

输出:

完成`Main`然后2秒后:

完成"发送邀请"。完成`CreateEvent`

如果我简单地将CreateEvent更改为:

public async Task<IActionResult> CreateEvent(Event val)
{
_ctx.Event.Add(val);
await _ctx.SaveChangesAsync();
Task.Run(() => SendInvitation(val));
return Ok();
}

然后我得到这个输出:

完成`Main`。完成`CreateEvent`然后2秒后:

完成"发送邀请">

这似乎是你想要的。

简单的答案是,您不能保证该代码的执行会完成。

这就是为什么ASP.NET Core有后台工作的基础设施:在.NET Core 2.x Web应用程序或微服务中使用IHostedService和BackgroundService类实现后台任务

相关内容

最新更新