Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are the SmtpClient.SendMailAsync methods Thread Safe?

The SmtpClient Class states that instance members are not thread safe. This can be seen if concurrent calls are made to Send or SendAsync. Both methods will throw a InvalidOperationException on the second call if the first has not yet completed.

The method SendMailAsync, introduced in .NET 4.5, does not list InvalidOperationException as a thrown exception. Do the new .NET 4.5 methods implement some sort of queuing? Reflector isn't able to shed any light on the implementation details of this class, so I assume this has been implemented in native methods.

Can multiple threads call the SendMessageAsync method on a shared instance of the SMTP client safely?

like image 805
brianfeucht Avatar asked Apr 08 '13 20:04

brianfeucht


People also ask

Is SmtpClient thread safe?

Since the SmtpClient is not fully thread safe and can not send multiple emails at the same time, the only way to build a scalable and performant solution is to make your own pool of SmtpClient instances. Write an EmailSender class and register as a singleton with your IOC container.

Is SmtpClient obsolete?

The SmtpClient type is obsolete on some platforms and not recommended on others; for more information, see the Remarks section.

What is SmtpClient?

An SMTP Client allows sending of e-mail notifications using a SMTP server. Simple Mail Transfer Protocol (SMTP) is a widely used protocol for the delivery of e-mails between TCP/IP systems and users. The SMTP Client can send e-mails to various recipients.


2 Answers

I'm not sure why using Reflector didn't work for you. If I decompile it, I see the following code:

[HostProtection(SecurityAction.LinkDemand, ExternalThreading=true)]
public Task SendMailAsync(MailMessage message)
{
    TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
    SendCompletedEventHandler handler = null;
    handler = delegate (object sender, AsyncCompletedEventArgs e) {
        this.HandleCompletion(tcs, e, handler);
    };
    this.SendCompleted += handler;
    try
    {
        this.SendAsync(message, tcs);
    }
    catch
    {
        this.SendCompleted -= handler;
        throw;
    }
    return tcs.Task;
}

As you can see, it's a simple TAP wrapper for SendAsync(). And if SendAsync() throws an exception, SendMailAsync() just rethrows it.

So, the conclusion is that SendMailAsync() is not thread-safe and that its exceptions are underdocumented.

like image 174
svick Avatar answered Sep 29 '22 00:09

svick


As a note (since I don't have enough points to comment), the traditional way to write an asynchronous operation was to use the Asynchronous Programming Model (APM), but today we typically use the Task-based Asynchronous Pattern (TAP) with its async/await keywords. And although it's not unusual to see TAP wrappers around APM methods, it's also possible to see APM wrappers around TAP methods.

like image 40
Moe Howard Avatar answered Sep 29 '22 00:09

Moe Howard