-2

I have a question regarding async await pattern. I have following code

internal async Task<int> ConsumedBytesAsync(byte[] buffer)
{
    while(condition)
    {
        //build message
        
        if (message != null)
        {
            if (_previousRegisterMessageTask != null)
            {
                await _previousRegisterMessageTask;
            }

            _previousRegisterMessageTask = _controllers.RegisterMessageAsync(message, chunk.MessageHeader.MessageStreamId);
        }
    }
    
    return totalConsumed;
}

And the code in other class.

internal async Task RegisterMessageAsync(IMessage message, uint messageid)
{
    // some stuff about retrieve resposible controller

    if (controller != null)
    {
        await controller.HandleMessage(message, action);
    }
}

I would like to increase performance by run RegisterMessage asynchronously and in the same time start build next message. When next message is built procecss should await for finish registration of previous message. In ideal world it should works fine but actually _controllers.RegisterMessageAsync method execute synchronously.

I've checked that by Stopwatch start and stop around that method (I assumed that elapsed time should be around 0 - 5 becuase of only operation which should be done is return and save Task but was 800-900 what is average execution time of this method) and Control.WriteLine within _controllers.RegisterMessageAsync after await and after save Task to _previousRegisterMessageTask (Control.WriteLine from _controllers.RegisterMessageAsync appear firstly.)

So please let me know why that operation works synchronously instead of asynchronously and what can i do to achieve logic I descriebe above. Maybe I did wrong tests and it is works asynchronously ? Thanks in advance.

Ditrikss
  • 37
  • 4
  • The while loop has an await on the task. This makes it sequential. (note, there is a difference between asynchronous and simultaneous). So --- can you mark in you're code exactly what you are trying to measure? As MVP? Because at the current setup I am tempted to blame the await in the loop for the behaviour. – Stefan Oct 10 '20 at 11:05
  • Yes sure. As I mentioned above I would like register message and build next message at the same time. I measured execution of time thats line -> _previousRegisterMessageTask = _controllers.RegisterMessageAsync(message, chunk.MessageHeader.MessageStreamId); -> By Stopwatch. I think the execution of that line should be 0-5 elapsed time because there should be only return Task and save in _previousRegisterMessageTask but the time was difrence. Elapsed time was 800-900 that is average execution time of this method. – Ditrikss Oct 10 '20 at 11:22
  • 1
    "_controllers.RegisterMessageAsync method execute synchronously." -> completely incorrect. You're confusing asynchronous operations with fire-and-forget processing. That entire code works asynchronously assuming `HandleMessage` is asynochronous. Also, notice that `_controllers.RegisterMessageAsync` starts to execute more or less at the time that `_previousRegisterMessageTask` is assigned the value. – Camilo Terevinto Oct 10 '20 at 11:23

2 Answers2

-2

The await operator suspends evaluation of the enclosing async method until the asynchronous operation represented by its operand completes. If you need parallel evaluation of two of more methods do not use await until you need the results of method evaluation. You could try next pattern (if you need process messages sequentially)

internal async Task<int> ConsumedBytesAsync(byte[] buffer)
{
    Message message = await BuildMessageAsync();
    int totalConsumed = 0;
    while (condition)
    {
        Task<Message> buildNextMessage = BuildMessageAsync();
        Task registerMessage = RegisterMessageAsync(message);
        message = buildNextMessage.IsCompleted ? buildNextMessage.Result : await buildNextMessage;
        if (!registerMessage.IsCompleted)
        {
            await registerMessage;
        }
        totalConsumed++;
    }
    return totalConsumed;
}

internal async Task<Message> BuildMessageAsync()
{
    // long running message build operation
    await Task.Delay(1000);
    return new Message { Text = DateTime.Now.ToString() };
}

internal async Task RegisterMessageAsync(Message message)
{
    // long running message processing 
    Console.WriteLine(message.Text);
    await Task.Delay(1000);
}

about await operator

Dmitry Kolchev
  • 2,116
  • 14
  • 16
  • 1
    "await suspends current thread". Not true. See e.g. [here](https://stackoverflow.com/questions/37278281/async-await-and-threads) – Klaus Gütter Oct 11 '20 at 04:45
-2

You need to create a list of tasks then get a task that encompasses all of these tasks:

List<Task> tasks = new List<Task>();

while (whatever) {
    task = controller.HandleMessage(message, action);
    tasks.Add(task);
}

await Task.WhenAll(tasks);
Tarik
  • 10,810
  • 2
  • 26
  • 40