Androme
Androme

Reputation: 2449

Why do async await without being told

I am trying to make part of my system run in parallel, but some some reason do it wait for each element before it starts the next even though i have not told it to await. I would like to start executing ExecuteItem for each this.Items and then continue when they are all done.

bool singleThread = false;
public async Task Execute()
{
        if (!this.singleThread)
        {
            var tasks = this.Items.Select(x => this.ExecuteItem(x)).ToArray();
            Task.WaitAll(tasks);
        }
        else
        {
            foreach (var item in this.Items)
            {
                await this.ExecuteItem(item);
            }
        }
}

private async Task ExecuteItem(IMyItem item)
{
        MappedDiagnosticsContext.Set("itemRef", item.ItemRef);
    try
    {
        await this.HandelItem(item);
    }
    catch (Exception exp)
    {
        Logger.ErrorException(string.Format("Execution for {0} failed.", item.ItemName), exp);
        Logger.Error("Error Message: ", exp.Message);
    }

    MappedDiagnosticsContext.Remove("itemRef");
}

To make clear my problem my code behaves as if had wrote the following

var tasks = this.Items.Select(x => await this.ExecuteItem(x)).ToArray();

To make sure it was not some kind of linq problem have i rewriten the problem code to the following, however the code still blocks tasks[i] = this.ExecuteItem(this.Items[i]);

        Task[] tasks = new Task[this.Items.Count];
        for (int i = 0; i < this.Items.Count; i++)
        {
            Console.WriteLine("Adding " + this.Items[i].ItemName);
            tasks[i] = this.ExecuteItem(this.Items[i]);
        }
        Console.WriteLine("Waiting!!!");
        Task.WaitAll(tasks);

Upvotes: 1

Views: 88

Answers (2)

shf301
shf301

Reputation: 31394

Something in HandelItem is blocking.

async methods don't run completely asynchronously, they execute synchronously up until the point they hit an await. So all of ExecuteItem, up to HandelItem will run before the tasks list is built. This synchronous behavior would continue into HandelItem if it is an async method, so likely HandelItem is executing while building up the tasks list.

This is easily seen with this example program:

static void Main(string[] args)
{
  var items = Enumerable.Range(1, 2);
  Console.WriteLine("Start");
  var tasks = items.Select(i => AsyncMethod(i)).ToArray();
  Console.WriteLine("Got tasks");
  Task.WaitAll(tasks);
  Console.WriteLine("Done!");
}

static async Task AsyncMethod(int i)
{
  Console.WriteLine("Enter {0}", i);
  await AsyncMethod2(i);
  await Task.Delay(1000);
  Console.WriteLine("Exit {0}", i);
}

static async Task AsyncMethod2(int i)
{
  Console.WriteLine("Enter2 {0}", i);
  await Task.Delay(2000);
  Console.WriteLine("Exit2 {0}", i);
}

It's output is:

Start
Enter 1
Enter2 1
Enter 2
Enter2 2
Got tasks
Exit2 2
Exit2 1
Exit 1
Exit 2
Done!

So both async methods run while building the task list, up until the point that they have to wait. So if HandelItem does something non-asynchronous, it will cause blocking.

Upvotes: 3

Tom
Tom

Reputation: 8180

If you want the tasks to execute in parallel; and wait until all are complete:

await Task.WhenAll(this.Items.Select(item=>this.ExecuteItem(item)));

Upvotes: 2

Related Questions