简体   繁体   English

使用 async/await 时,我的控制台应用程序过早关闭?

[英]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.
}

Here is an article with further explanation.这是一篇进一步解释的文章

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.

相关问题 在控制台应用程序中使用 WindowsFormsSynchronizationContext 时出现异步/等待死锁 - async/await deadlock when using WindowsFormsSynchronizationContext in a console app 在控制台应用程序中异步并等待 - async and await in console app 在控制台应用程序中使用async / await时,为什么需要AsyncContext? - Why is AsyncContext needed when using async/await with a console application? 控制台应用程序 async/await 不返回我的列表 - console application async/await not returning my list 在Windows Phone上使用异步/等待时的应用程序体系结构 - App architecture when using async/await on Windows Phone 即使我的方法不使用并发,async/await 仍然有用吗? - Is async/await still useful even when my method is not using concurrency? 使用异步等待时,Console.Writeline仅在某些时候写入控制台 - Console.Writeline is only writing to the console some of the times when using async await 进程文件列表在C#控制台应用程序中异步使用async和await - Process List of Files Asynchronously using async and await in C# Console App C#异步/等待控制台应用程序中的奇怪行为 - C# async/await strange behavior in console app 为什么此控制台应用程序中的异步等待模式会导致死锁? - Why does async await pattern in this console app caused a deadlock?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM