How asynchronous is SmtpClient.SendAsync?

In a previous role, I was tasked with writing a newsletter email sender. How hard can this be, I thought to myself, and set off to complete my mission.

Initial Thoughts

We were probably going to be sending tens of thousands of emails at a time so although I figured I’d need to use some threading, I thought I’d start by using the asynchronous version of the SmtpClient.Send method, SmtpClient.SendAsync instead of the blocking SmtpClient.Send. I figured that way I’d be able to send batches of emails asynchronously and that way we’d be able to get through all the emails super fast.

The Test

I wrote some basic prototype code using SmtpClient.SendAsync and ran a test to send a couple hundred emails in it. Although my test ran without errors, I pretty quickly discovered that I wasn’t receiving all the emails that I was apparently sending by calling SmtpClient.SendAsync! At first I wondered if I’d somehow overflowed my inbox or something but that didn’t seem to be the problem. Then I started doing a bit more research and discovered something which explained the behaviour I was seeing…

The Findings

From the ‘remarks’ section of the MSDN documentation on SmtpClient.SendAsync:

After calling SendAsync, you must wait for the e-mail transmission to complete before attempting to send another e-mail message using Send or SendAsync.

And…

To receive notification when the e-mail has been sent or the operation has been canceled, add an event handler to the SendCompleted event.

OH! So in order to successfully send multiple emails asynchronously, I must add an event handler to the SmtpClient.SendCompleted event and wait until the first SmtpClient.SendAsync has completed before triggering the next one. Hmm… this does not seem all that asynchronous to me! I realize that there must be good reasons why it was implemented this way but in theory, using SmtpClient.SendAsync to send multiple emails really isn’t all that much more asynchronous than lining up a bunch of synchronous calls to SmtpClient.Send.

Conclusion

I guess it all comes down to your interpretation of what ‘asynchronous’ means – in this case, SmtpClient.SendAsync is indeed asynchronous in that it allows the program to carry on executing without blocking. This is great in most cases, unless what you want to do next is send another email.

So to sum up, it seems the only way to send multiple emails at the same time using .NET’s SmtpClient is to use threading after all. Spawn up a few worker threads with a separate instance of the SmtpClient in each and just send the emails using SmtpClient.Send.

If you liked this post, share it!
Facebook Twitter Linkedin Delicious Digg Stumbleupon Email

23 thoughts on “How asynchronous is SmtpClient.SendAsync?

  1. You are right with SMTP the only way to keep issuing new email send requests is to spawn new threads. In cases where the recipient address has a different domain. (I will get back to this later) But this is not a .NET problem it is a protocol problem.

    SMTP is a very chatty protocol which requires a good deal of talking between the client and the server. This talking, keeps a connection open on the port and IP that you are sending from, and since the connection is open and actively talking the thread is locked up until the client or server sends the session.

    I said I would get back to this earlier, with the SMTP protocol it allows you to send multiple messages per connection, I am not talking about a bunch of CC requests, you can actually send completely different messages over the same connection and thread as long as the recipients are the same domain, but the SMTP client has to be intelligent enough to keep the connection open and do this. The .NET client isn’t.

    There are ways around this problem, the biggest one is latency between the client and relay server, if you reduce that to near zero, you will have a very quick sending experience. A relay server is what ever you send your mail through. So if you send your mail through Gmail, smtp.gmail.com is your relay server, if you send your mail through Exchange, Exchange is your relay server.

    The best way to reduce your latency to zero is to run an SMTP relay on your server. The SMTP relay will receive the messages from your application and then manage all these connections, this works really well for connections with high latency. It is very easy and takes almost no work, because you just install the built in IIS SMTP server, or if you want to use an external sender like Exchange you just hook the IIS SMTP server up as a smart host.

    http://coderjournal.com/2010/10/easy-mail-delivery-with-smtp-smart-host/

    Hope this helps.

    • @Nick Berardi: Wow, what a great comment. Thanks for all that info Nick – you’ve clearly done your research in this area. If I ever have to bulk send emails again I will certainly look into doing this way. Thanks! :)

    • @Nick Berardi: Resurrecting an old post/thread. Your solution for SMTP Relay worked beautifully sir. With exchange tarpit settings, the need to pull student email list from oracle as the only means of mass email, and the desire to have it built into a web application the configuration was stacked against me for making a fast bulk email system that didn’t fall prey to recycling mid process or simply timing out. Threaded sends hit connection limits and crashed worker processes to easily. I setup the localhost smtp, secured it for localhost connections inbound/outbound for a bit of security, and now the bulk email process resolves instantly. Thank you!

      • @Annie: Oops.. I have to read your post more carefully ;). But I actually ment something else… You can still call the SendAsync method on each instance, without having to create all these new threads.

      • @Jeroen Kok: Ahh yeah I see what you mean – yup, I can’t see why that wouldn’t work, it’s pretty much the same result. At the end of the day, you just need to have several SmtpClient objects. If you do it with threads, you have to use the Send method. If you just create SmtpClient objects in a for loop you should use SendAsync.

        Interestingly, in .NET 4, SmtpClient now implements IDisposable (it hasn’t in previous versions of .NET) so in either scenario we’ll now need to wrap them up in using statements!

  2. What an idiotic article… Being able to reuse an instance before it has completed its work has absolutely nothing to do with asynchrony!

    • @P: Sorry to hear you feel that way. I’m a bit disappointed that you weren’t brave enough to leave your name or own website but that’s ok. You’re right, in this case the term ‘asynchronous’ still applies. But unless you’ve worked with the SmtpClient class before, this can catch you out. And even worse, it’s not always obvious that not all your emails are being sent. There’s a reason Microsoft thought to give this a special mention in the remarks.

  3. Hi Annie,

    That was a great article!!. Exactly i am facing the same problem now. I am trying to send hundered’s of mails using smtpClient.SendAsync() but not able to send all mails. The same code when used with smtpClient.Send() is working fine.

    As stated in microsoft document…
    “After calling SendAsync, you must wait for the e-mail transmission to complete before attempting to send another e-mail message using Send or SendAsync.”
    In order to wait for the email to send completely, then what is the use of sending asynchronously??

    From your conclusion, creating multiple instances of smtpclients?? can you guide me on this please??

    Thank You.

  4. SMTP is no the best way for big Mailings. You are still a “Client” from Mailserver. So it depends from Mailserver. +1 Nick Berardi eiser SMTP relay or IMAP.

  5. @Swapna, try something like this:

    foreach (MailMessage email in emails)
    {
    SmtpClient client = new SmtpClient();

    // ... set up stuff for client
    // dispose the email message and sender after message has been sent
    client.SendCompleted += (s, e) => {
    if (e.Error != null)
    {
    // log error
    }
    client.Dispose();
    email.Dispose();
    };
    // send the message asynchronously
    client.SendAsync(message, null);
    }

    Here you’ll see we’re creating a separate SmtpClient for each email being sent. I find this a little bit more readable than trying to send the next email inside the SendCompleted callback, but each to their own.

    • @Andrew:

      I am not sure there will have errors when you dispose a MailMessage in there loop.

      I mean the emails is big enough. the SendCompleted delegate will excuted in the loop. this time to Dispose is the right place?

      • @steve.j.yin: email.dispose() happens at the end of the callback of each email so would be fine wouldn’t it. Also, there’s a typo on the last line – it should be `client.SendAsync(email, null);`

  6. Hi!

    Thanks for your article, you probably saved me some headaches! :). I wrote a simple Gist with how I would do this (though I didn’t try it) with the new async features in .NET 4.5.

    What do you think??

    • Hey Martín!

      I found your Gist and I must say I haven’t used the new SendMailAsync method introduced in .NET 4.5. Given that the MSDN documentation doesn’t mention anything about needing to wait for the previous email to finish being sent before sending another one, I’m guessing this new method doesn’t suffer from the same problem due to the async/await technology under the covers. Have you tested it out to verify?

Leave a Reply

Your email address will not be published. Required fields are marked *