Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sending 5000 messages in async way in C#

Tags:

c#

async-await

I have to send 10000 messages. At the moment, it happens synchronously and takes up to 20 minutes to send them all.

// sending messages in a sync way
foreach (var message in messages)
{
    var result = Send(message);
    _logger.Info($"Successfully sent {message.Title}.")
}

To shorten the message sending time, I'd like to use async and await, but my concern is if C# runtime can handle 15000 number of tasks in the worker process.

var tasks = new List<Task>();
foreach (var message in messages) 
{
    tasks.Add(Task.Run(() => Send(message))
}

var t = Task.WhenAll(tasks);
t.Wait();
...

Also, in terms of memory, I'm not sure if it's a good idea to create a list of 15000 tasks

like image 346
Andrew Chaa Avatar asked Dec 22 '17 10:12

Andrew Chaa


People also ask

Does async await use thread pool?

The async and await keywords don't cause additional threads to be created. Async methods don't require multithreading because an async method doesn't run on its own thread. The method runs on the current synchronization context and uses time on the thread only when the method is active.

How do you call async method in page load?

How can I call a async method on Page_Load ? If you change the method to static async Task instead of void, you can call it by using SendTweetWithSinglePicture("test", "path"). Wait() . Avoid async void unless you are using it for events.

When should you use async await?

Async/Await makes it easier to write promises. The keyword 'async' before a function makes the function return a promise, always. And the keyword await is used inside async functions, which makes the program wait until the Promise resolves.


1 Answers

Since I came home from work, I have played with this a bit and here is my answer.

First of all Parallel.ForEach is bretty cool to use, and with my 8 core runs very fast.

I suggest to limit the CPU usage so you do not use 100% capacity, but that depends on your system, I have made two suggestion for it.

The other things is you need to monitor and be sure that your sender server can eat all these jobs with out getting trouble.

Here is a the implementation:

public void MessMessageSender(List<Message> messages)
{
    try
    {
        var parallelOptions = new ParallelOptions();
        _cancelToken = new CancellationTokenSource();
        parallelOptions.CancellationToken = _cancelToken.Token;
        var maxProc = System.Environment.ProcessorCount;
        // this option use around 75% core capacity
        parallelOptions.MaxDegreeOfParallelism = Convert.ToInt32(Math.Ceiling(maxProc * 0.75));
        // the following option use all cores expect 1
        //parallelOptions.MaxDegreeOfParallelism = maxProc - 1;
        try
        {
            Parallel.ForEach(messages, parallelOptions, message =>
            {
                try
                {
                    Send(message);
                    //_logger.Info($"Successfully sent {text.Title}.");
                }
                catch (Exception ex)
                {
                    //_logger.Error($"Something went wrong {ex}.");
                }
            });
        }
        catch (OperationCanceledException e)
        {
            //User has cancelled this request.
        }
    }
    finally
    {
        //What ever dispose of clients;
    }
}

My answer is inspired for this page.

Documentation:

  • Parallel.Foreach
  • Environment.ProcessorCount
like image 162
Maytham Avatar answered Oct 13 '22 15:10

Maytham