Confused on how to structure async/await code that

2019-07-23 12:52发布

问题:

I read the documentation for async-await feature, but still highly confused on how to structure async-await code that calls SmtpClient.SendMailAsync() method.
How should I rewrite the code to properly use async-await ?

Currently, my code looks like this:
Periodically PrivateSignal is being called by the system to send out email notifications.

public override void PrivateSignal(IEventInformation ev) 
{
    TagEvent tag = (TagEvent)ev;
    Task.Run(async () =>
    {
        try
        {
           await smptClient.SendMailAsync(CaptureRC.SmptFromEmailAddr,
                                    ToEmails,
                                    CaptureRC.EmailSubject,
                                    "seen moving" + tag.ToString());
        }
        catch (AggregateException ex)
        {
           //TODO handle the error
           //TODO log the erros, along with
        }
    }).Wait();
}

回答1:

You should be making PrivateSignal async as well which includes marking it with async and returning a Task:

public override async Task PrivateSignalAsync(IEventInformation ev) 
{
    TagEvent tag = (TagEvent)ev;
    try
    {
       await smptClient.SendMailAsync(CaptureRC.SmptFromEmailAddr, ToEmails, CaptureRC.EmailSubject, "seen moving" + tag.ToString());
    }
    catch (Exception ex)
    {
        // ...
    }
}

It seems, though, that you can't do that since PrivateSignal is overriding an already synchronous method. If you can change the base method to return a Task then you should, but if you can't then simply don't use async at all inside that method because blocking on async can lead to deadlocks and other unwanted results:

public override void PrivateSignal(IEventInformation ev) 
{
    TagEvent tag = (TagEvent)ev;
    try
    {
       smptClient.SendMail(CaptureRC.SmptFromEmailAddr, ToEmails, CaptureRC.EmailSubject, "seen moving" + tag.ToString());
    }
    catch (Exception ex)
    {
        // ...
    }
}


回答2:

async/await is the new hotness to replace things like Task.Run and Task.Wait. Try this:

public async override Task PrivateSignal(IEventInformation ev) 
{
    TagEvent tag = (TagEvent)ev;

    try
    {
        await smptClient.SendMailAsync(CaptureRC.SmptFromEmailAddr,
            ToEmails, CaptureRC.EmailSubject, "seen moving" + tag.ToString());
    }
    catch (Exception ex)
    {
        //TODO handle the error
        //TODO log the erros, along with
    }
}

The await causes the running thread to suspend until the Task completes (or is cancelled or experiences an error). The rest of the method is registered as a continuation automatically. async/await is therefore syntactic sugar for task-based asynchronous programming.

Also, it's important to avoid async void methods since the void support is reserved for event handlers. Instead of void, return Task for methods with no return value.

Finally, since this method is an override, you'd need to change the base method's return type to Task as well.