-3

I'm trying to send any number of emails in the shortest amount of time using .NET 5.0?

I've been playing with something like the following but I am not sure if it is optimal or even correct as there are a number of elements I don't understand.

public async Task SendEmailAsync(string subject, string htmlMessage,
    IEnumerable<string> recipients, string? attachment)
{
    using SemaphoreSlim semaphore = new(10, 10);
    await Task.WhenAll(recipients.Select(async recipient =>
    {
        await semaphore.WaitAsync();

        try
        {
            return SendEmailAsync(subject, htmlMessage, recipient, attachment);
        }
        finally
        {
            semaphore.Release();
        }
    }));
}

Can someone clarify if this is correct, or let me know if they know a better approach?

8
  • @Jonathon I can help you with this, but you'll need to edit the question so it can be re-opened. Commented Jul 8, 2021 at 20:47
  • 1
    @glenebob: I don't know how else to ask how to do this. Do I just get rid of the code I have so far? Conceptually, this question is dead simple. I don't understand the problem. Commented Jul 8, 2021 at 20:49
  • Would this might solve your what you are asking for stackoverflow.com/questions/47939504/… Commented Jul 8, 2021 at 21:04
  • @maytham-ɯɐɥʇʎɐɯ: This is an older question. I was under the impression the newer constructs were preferred, but because they are newer constructs, I'm having a bit of trouble getting authoritative information on this. Commented Jul 8, 2021 at 21:07
  • @Jonathon try asking something like "using concurrent tasks to execute an async method in a controlled fashion". This isn't really a threading problem when you can use asynchronous email services, and it isn't really about email either. Also, sending an email within a Select predicate is, uh, rather abusive. I have a working sample for you, I just need a way to get it to you. Commented Jul 8, 2021 at 21:40

1 Answer 1

1

If you want to send the emails in the shortest amount of time, you should probably try first to send them all at once like this:

public async Task SendEmailAsync(string subject, string htmlMessage,
    IEnumerable<string> recipients, string? attachment)
{
    await Task.WhenAll(recipients.Select(async recipient =>
    {
        await SendEmailAsync(subject, htmlMessage, recipient, attachment);
    }));
}

This will invoke the SendEmailAsync method for all recipients concurrently, and practically instantly. It can't get much faster than that!

Your current code that employs a SemaphoreSlim implies that your email delivery service chokes when bombarded with multiple emails concurrently, and for this reason you have limited the concurrency to 10. Your code is excellent at limiting the concurrency of the asynchronous operations (provided that you fix the missing await before the SendEmailAsync call), but whether this throttling helps at improving the throughput of your email delivery service can only be found by experimentation.

Also, as @Krythic mentioned in a comment, the email service providers may enforce non-technical limitations at how many emails they can accept for delivery, to combat the phenomenon of spam.

Btw there is no multithreading involved in your code. The SendEmailAsync is invoked multiple times sequentially on the current thread, and the asynchronous email deliveries are most likely flying on no thread at all.

Sign up to request clarification or add additional context in comments.

12 Comments

But what if there were 1,000 emails? Isn't some sort of throttling required?
@JonathanWood you should check the specifications of your email delivery service. It may be able to handle 1,000,000 concurrent requests with ease.
Thanks, but your code gives me a CS1998 warning at the =>: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' keyword operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
This approach can result in a very unreasonable number of tasks active at once. It has nothing to do with the email service because you cannot know how many other clients are also trying to send emails. You should find an approach that limits the number of tasks. I'd guess something between 10 and 50 tasks might be reasonable.
Just wanted to add that if you programatically send too many emails at once, Gmail or whatever will straight up block you. I once wrote a program to send out mass emails, and gmail blocked it after a dozen or so emails. Not sure how the system works, but they do indeed have mechanisms in place to prevent this.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.