C# 使用async/await时我的控制台应用程序过早关闭?
我有一个控制台应用程序,它所做的就是循环所有客户,向特定客户发送电子邮件,然后关闭。我注意到MailKit中的一些函数提供异步功能,所以我尝试使用它们而不是非aysnc,当我这样做时,它在上执行了第一条语句(emailClient.ConnectAsync),然后我注意到我的控制台应用程序正在关闭。它没有坠毁。执行返回到Main()并在调用my SendReports()函数后继续执行C# 使用async/await时我的控制台应用程序过早关闭?,c#,multithreading,mailkit,C#,Multithreading,Mailkit,我有一个控制台应用程序,它所做的就是循环所有客户,向特定客户发送电子邮件,然后关闭。我注意到MailKit中的一些函数提供异步功能,所以我尝试使用它们而不是非aysnc,当我这样做时,它在上执行了第一条语句(emailClient.ConnectAsync),然后我注意到我的控制台应用程序正在关闭。它没有坠毁。执行返回到Main()并在调用my SendReports()函数后继续执行 private static void Main(string[] args) { ... var r
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;
}
}
}
我不明白的是,为什么我的循环不能继续在所有客户之间循环还有更多的工作要做。不知道为什么会跳回到主函数
我所希望的是,循环将继续通过所有客户发送电子邮件;不必等待电子邮件发送后再继续下一封
我感谢你的帮助
我所希望的是,这种循环会一直持续下去
客户和发送电子邮件;不必等待电子邮件发送
在继续下一步之前
你的循环没有这样做。它实际上是一次发一封电子邮件。但是,调用线程正在立即获得控制权
下面这一行实际上是“在另一个线程上执行此操作,完成后继续运行此方法”。同时,由于await
没有阻塞,因此调用线程继续执行
await SendReport(customer.Id, repTypeId, freqName);
我建议这样做,启动所有报告并执行等待完成所有报告:
await Task.WhenAll(_dbContext.Customer.Select(x => SendReport(x.Id, repTypeId, freqName));
我还强烈建议在使用async
时始终返回任务
:
public async Task SendReportsToCustomers()
这样,调用者就可以等待
,或者对返回值做任何它想做的事情。您甚至可以使用Task.Result
对其进行阻止
我不明白的是为什么我的循环不能继续在所有的地方循环
顾客还有更多的工作要做。不知道为什么会跳过
回到主功能
请参阅wait
,以更好地了解它的用法和非阻塞性
在一个非常高的层次上,您可以将await
想象为“在任务完成之前不要执行此方法的其余部分,但也不要阻止它”。您似乎不理解await
的工作原理。每次对函数调用wait
,调用方函数本身都会临时返回,直到等待的函数返回
这意味着,除非等待的函数及时返回,否则主线程将完成执行main
,并退出应用程序。要解决此问题,您需要将SendReportsToCustomers()
的返回类型更改为类似以下内容:
public async Task SendReportsToCustomers()
现在,你可以等它完成了。以阻塞方式:
reportServices.SendReportsToCustomers().Result();
或
或者在使用等待
的非阻塞等待中:
await reportServices.SendReportsToCustomers();
首先,应该异步void方法,ReportsServices.SendReportsToCustomers()
应该返回Task
。您的代码调用该方法但不等待它完成,该方法是异步的,因此它在第一次等待时返回。您应该在Main()中等待它完成。有两种方法可以做到这一点:
如果您使用的是C#7,则允许使用async Main:
专用静态异步任务主(字符串[]args)
{
...
var reportServices=new reports服务();
等待reportServices.SendReportsToCustomers();
Log.CloseAndFlush();//它执行第一个等待调用,然后返回此处。
}
如果没有,则必须同步等待操作完成:
private static void Main(字符串[]args)
{
...
var reportServices=new reports服务();
reportServices.SendReportsToCustomers().Wait();;
Log.CloseAndFlush();//它执行第一个等待调用,然后返回此处。
}
这里有一个更详细的解释。正如其他人所解释的,控制台应用程序不会等待异步方法完成。它将直接跳到下一行代码。如果它到达您的主要功能的末尾,控制台应用程序将结束
也许您可以在主功能的末尾添加一个Console.ReadLine()
,以防止控制台应用程序突然终止。是非阻塞的。调用线程继续并行运行,而不是方法的其余部分。调用SendReportsToCustomers
的任何程序都不能等待它,因为它不会返回任务。如果您想阻止使用任务.Result
我仍在努力解决这个问题
我建议先从my开始,然后再从my开始。忽略SendReport(任务的返回值)不是很好的做法或设计。您的意思是使用任务.Wait()
不是很好的做法?您想详细说明一下吗?您的foreach循环正在调用SendReport
并忽略返回值。您只是从中删除了wait
。但为什么这很重要?使用如何选择和任务。所有何时实际处理返回值?因为我使用等待。。。链接异步
方法背后的思想是链接等待
调用。或者,至少对返回的任务执行一些操作。你完全忽略了它。充其量这是一个糟糕的设计。最坏的情况是一只虫子。这正是您应该避免声明为async void
的方法的原因。我实现了该方法,它成功了。感谢问题:为什么现在要等待SendReportToCustomers完成后才能继续?我认为它会继续并关闭应用程序。
await reportServices.SendReportsToCustomers();