[英]My console app shutdown prematurely when using async / await?
I have a console app that all it does is loop through all the customers and send emails to specific ones and then shutdown.我有一个控制台应用程序,它所做的只是遍历所有客户并向特定客户发送电子邮件,然后关闭。 I noticed that some of the functions in MailKit's offer asynchronous so I tried using them instead of non-aysnc and when I did that, it executed the first statement (emailClient.ConnectAsync) on and then I noticed that my console app was shutting down.
我注意到 MailKit 中的某些功能提供异步功能,因此我尝试使用它们而不是非 aysnc,当我这样做时,它执行了第一条语句 (emailClient.ConnectAsync),然后我注意到我的控制台应用程序正在关闭。 It didn't crash.
它没有崩溃。 The execution returned to Main() and continued executing after my call to my SendReports() function.
执行返回到 Main() 并在我调用 SendReports() 函数后继续执行。
private static void Main(string[] args)
{
...
var reportServices = new ReportsServices();
reportServices.SendReportsToCustomers();
Log.CloseAndFlush(); // It executes the first await call then returns here.
}
internal class ReportsServices
{
public async void SendReportsToCustomers()
{
try
{
foreach (var customer in _dbContext.Customer)
{
...
await SendReport(customer.Id, repTypeId, freqName);
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
private async Task SendReport(int customerId, int repTypeId, string freqName)
{
try
{
...
var es = new EmailSender();
await es.SendAsync();
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
public class EmailSender
{
public async Task SendAsync()
{
try
{
var message = new MimeMessage();
...
using (var emailClient = new SmtpClient())
{
await emailClient.ConnectAsync("smtp.gmail.net", 587);
await emailClient.AuthenticateAsync("username", "password"); // If I put a debug break here, it doesn't hit.
await emailClient.SendAsync(message);
await emailClient.DisconnectAsync(true);
// If I use the following calls instead, my console app will not shutdown until all the customers are sent emails.
await emailClient.Connect("smtp.gmail.net", 587);
await emailClient.Authenticate("username", "password"); // If I put a debug break here, it doesn't hit.
await emailClient.Send(message);
await emailClient.Disconnect(true);
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
What I don't get is why wouldn't my loop continue looping through all the customers?我不明白的是为什么我的循环不会继续遍历所有客户? - there is more work for it.
- 还有更多的工作要做。 Not sure why it would skip back to the Main function.
不知道为什么它会跳回到 Main 函数。
What I was hoping for is that loop would continue through all the customers and send emails;我希望这个循环会继续通过所有客户并发送电子邮件; not having to wait for the email to be sent before continuing on the next one.
在继续处理下一封邮件之前,不必等待电子邮件发送完毕。
I appreciate your help!我感谢您的帮助!
It seems that you do not understand how await
works.看来你不明白
await
是如何工作的。 Each time you call await
on a function, the caller function itself will temporarily return, until the awaited function returns.每次对函数调用
await
时,调用者函数本身都会暂时返回,直到等待的函数返回。
This will mean that, unless the awaited function returns in time, your main thread will finish executing Main
and exit the application.这意味着,除非等待的函数及时返回,否则您的主线程将完成执行
Main
并退出应用程序。 To fix this, you need to change your return type for SendReportsToCustomers()
to something like this:要解决此问题,您需要将
SendReportsToCustomers()
返回类型更改为如下所示:
public async Task SendReportsToCustomers()
Now, you can actually wait for it to finish.现在,您实际上可以等待它完成。 In a blocking way:
以阻塞方式:
reportServices.SendReportsToCustomers().Result();
or或者
reportServices.SendReportsToCustomers().Wait();
or in a non blocking wait with await
:或在非阻塞等待中使用
await
:
await reportServices.SendReportsToCustomers();
First, you should AVOID async void methods, ReportsServices.SendReportsToCustomers()
should return Task
.首先,你应该避免异步无效方法,
ReportsServices.SendReportsToCustomers()
应该返回Task
。 Your code invokes the method but does not wait for it to finish, the method is asynchronous so it returns on the first await.you should wait for it to finish in Main().您的代码调用该方法但不等待它完成,该方法是异步的,因此它在第一个等待时返回。您应该在 Main() 中等待它完成。 There are 2 ways to do it:
有两种方法可以做到:
If you'are using C# 7, async Main is allowed:如果您使用的是 C# 7,则允许使用 async Main:
private static async Task Main(string[] args)
{
...
var reportServices = new ReportsServices();
await reportServices.SendReportsToCustomers();
Log.CloseAndFlush(); // It executes the first await call then returns here.
}
If not, you'll have to synchronously wait for the operation to finish:如果没有,您必须同步等待操作完成:
private static void Main(string[] args)
{
...
var reportServices = new ReportsServices();
reportServices.SendReportsToCustomers().Wait();;
Log.CloseAndFlush(); // It executes the first await call then returns here.
}
What I was hoping for is that loop would continue through all the customers and send emails;
我希望这个循环会继续通过所有客户并发送电子邮件; not having to wait for the email to be sent before continuing on the next one.
在继续处理下一封邮件之前,不必等待电子邮件发送完毕。
Your loop isn't doing this.你的循环没有这样做。 It's actually doing one email at a time.
它实际上是一次处理一封电子邮件。 However, the calling thread is getting control back immediately.
但是,调用线程会立即重新获得控制权。
This line below effectively reads "do this on another thread, then continue running this method once that's complete".下面这一行实际上是“在另一个线程上执行此操作,然后在完成后继续运行此方法”。 In the meantime, since
await
does not block, the calling thread continues execution.同时,由于
await
没有阻塞,调用线程会继续执行。
await SendReport(customer.Id, repTypeId, freqName);
I would suggest this instead, which launches all your reports and does an await
for completion of all of them:我建议这样做,它会启动您的所有报告并
await
完成所有报告:
await Task.WhenAll(_dbContext.Customer.Select(x => SendReport(x.Id, repTypeId, freqName));
I would also highly suggest to always return a Task
when using async
:我还强烈建议在使用
async
时始终返回一个Task
:
public async Task SendReportsToCustomers()
This way the caller can await
or do whatever it wants with the return value.这样,调用者可以
await
或对返回值做任何想做的事情。 You can even block on it with Task.Result
.您甚至可以使用
Task.Result
阻止它。
What I don't get is why wouldn't my loop continue looping through all the customers?
我不明白的是为什么我的循环不会继续遍历所有客户? - there is more work for it.
- 还有更多的工作要做。 Not sure why it would skip back to the Main function.
不知道为什么它会跳回到 Main 函数。
See await
documentation to better understand it's usage and non-blocking nature.请参阅
await
文档以更好地了解它的用法和非阻塞性质。
At a very high level, you can think of await
as saying "don't execute the rest of this method until this Task completes, but don't block either".在非常高的层次上,您可以将
await
视为“在此 Task 完成之前不要执行此方法的其余部分,但也不要阻塞”。
As explained by others, the console app will not wait for the async methods to finish.正如其他人所解释的,控制台应用程序不会等待异步方法完成。 It will jump straight to the next line of code.
它将直接跳转到下一行代码。 If it reaches the end of your main function, the console app will end.
如果它到达 main 函数的末尾,则控制台应用程序将结束。
Maybe you can add a Console.ReadLine()
at the end of your Main function to prevent your console app from ending abruptly.也许您可以在 Main 函数的末尾添加一个
Console.ReadLine()
以防止您的控制台应用程序突然结束。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.