为什么异步方法总是阻塞线程?

时间:2018-04-05 09:03:20

标签: c# asp.net

我有一个像这样的异步方法:

public static async Task<bool> SendMailAsync(){
...something
}

此方法返回结果的时间很长。

我还有其他方法:

public async Task<string> ThisIsController(){
Task<bool> result = SendMailAsync();
OtherMethod1();
OtherMethod2();
....
}

我没有在结果中使用await。但是“ThisIsController”总是等待结果返回然后运行OtherMethod1(),OtherMethod2? 谢谢

UPDATE1: SendMailAsync()的完整代码:

public static async Task<bool> SendMailAsync(List<string> listEmail, string subject, string body)
    {
        try
        {
            var emailsPerMin = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["EmailsPerMin"]
                .ToString());
            for (int i = 1; i <= System.Math.Ceiling(listEmail.Count * 1.0 / emailsPerMin); i++)
            {

                List<string> sendingList = listEmail.Skip((i - 1) * emailsPerMin).Take(emailsPerMin).ToList();
                foreach (var email in sendingList)
                {
                    MailHelper.SendMail(email, subject, body);
                    Thread.Sleep(60 * 1000);
                }
            }
            return true;
        }
        catch
        {
            return false;
        }
    }

4 个答案:

答案 0 :(得分:3)

添加async方法(单独使用)使代码异步。异步代码是 hard - async修饰符所做的就是启用await关键字(并启用一些关于返回类型的编译器魔法)。如果该方法实际上没有异步执行任何操作,则不会异步。特别是:

  • 如果方法不是await:它在async意义上真的不同步
    • (那里有一点警告:如果方法不是标记为async,而只是来自另一个return sa Task[<T>] 方法,然后它可能公开异步行为)
  • 如果方法 await,但所有等待的事情总是实际上同步完成:它不是真正的异步

在这种情况下,它是第一颗子弹。代码中没有await。事实上,编译器应该已经给你一个警告:

  

Warning CS1998 This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

即使方法返回Task[<T>](或其他一些等待模式):如果它不是实际异步(因为上面的任何一个项目符号):它将运行完成之前它返回给调用者;当它这样做时,它将被称为已经完成。

要使此代码异步,它所做的事情(可能是发送)需要是异步的。也许:

foreach (var email in sendingList)
{
    await MailHelper.SendMailAsync(email, subject, body);
    await Task.Delay(60 * 1000);
}

请注意;如果您的MailHelper没有async API, 也可以通过延迟异步使异步:

foreach (var email in sendingList)
{
    MailHelper.SendMail(email, subject, body);
    await Task.Delay(60 * 1000);
}

最后的想法:隐藏异常细节几乎总是不好。

答案 1 :(得分:2)

SendMailAsync将同步运行,直到第一次等待阻止。

将标记方法标记为“异步”实际上并不能神奇地以异步方式运行。你必须在该方法中使用等待

您的代码是同步的,可以在另一个线程中运行它,而无需更改您可以使用的SendMailAsync中的签名或代码。但理想情况下,您应该重写SendMailAsync以使用MailSender的异步API

var resultTask = Task.Run(async () => await SendMailAsync());
OtherMethod1();
OtherMethod2();
var result = await resultTask;

答案 2 :(得分:0)

如果你等待一个OtherMethod1(),他们将等待Method完成,然后他将执行OtherMethod2()。但是,如果你不等待它可能是Method1没有完成,Method2将启动。这取决于Method1和2中的内容。

答案 3 :(得分:0)

有几个后台作业处理库。我的一些项目使用了hangfire。

但是如果你想简化这个,你可以使用asp.net内置的东西。

HostingEnvironment.QueueBackgroundWorkItem((ct) =>
{
     SendMailAsync();
});

OtherMethod1();
OtherMethod2();

确保添加对System.Web.Hosting的引用

您不需要SendMailAsync()来返回任务,因为您不会等待某事。只需发送电子邮件代码即可在后台运行。