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.
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.
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…
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.
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.
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.