简体   繁体   中英

Why Async Method always blocking thread?

I have a method async like this:

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

This method is very long time to return result.

I have other method like this:

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

I'm not use await in result. But "ThisIsController" always wait result return then run OtherMethod1(), OtherMethod2? Thank you

Update1: Full code of 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;
        }
    }

Adding the async method does not (by itself) make code asynchronous. Async code is hard - all that the async modifier does is enable the await keyword (and enable some compiler magic about the return type). If the method doesn't actually do anything asynchronous, it won't be asynchronous . In particular:

  • if a method doesn't await : it won't really be asynchronous in the async sense
    • (minor caveat there: if a method isn't marked async , and simply return sa Task[<T>] from another method, then it might expose an asynchronous behaviour)
  • if a method does await , but all of the things being awaited always actually completed synchronously: it isn't truly asynchornous

In this case, it is the first bullet. There is no await in the code. In fact, the compiler should already be giving you a warning about this:

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.

Even if a method returns Task[<T>] (or some other awaitable pattern): if it isn't actually asynchronous (because of either of the bullets above): it will run to completion before it returns to the caller; and when it does so, it will be known as already complete.


For this code to be asynchronous, something it does (presumably the send) would need to be asynchronous. Perhaps:

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

Note; if your MailHelper doesn't have an async API, you could also make it asynchronous just by making the delay asynchronous:

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

Final thought: hiding exception details is almost always bad.

SendMailAsync will run synchronously until first await block inside.

Marking method as "async" doesn't actually make it magically to run asynchronously. You have to use await inside that method

Your code is synchronous, to run it in another thread without changing signature or code in your SendMailAsync you can use. But ideally you should rewrite SendMailAsync to use async API of MailSender

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

if you await a OtherMethod1() they will wait till the Method is finished, then he will execute OtherMethod2(). But if u dont await it could be that Method1 is not finished and Method2 will start. This depends on what is inside in Method1 and 2.

There are several background jobs processing library out there. I have used hangfire for some of my projects.

But if you want to make this simple, you can use something built-in inside asp.net.

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

OtherMethod1();
OtherMethod2();

Make sure you add reference to System.Web.Hosting

You do not need SendMailAsync() to return a Task since you will not be awaiting on something. Just let the sending of email code run in background.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM