Yasir
Yasir

Reputation: 1625

Error in using await operator within an async lambda expression

I am trying to load some arbitrary GPS data (150 million records) into Azure Table storage using Parallel.For and async await. But I am getting the following error in the first await statement:

The 'await' operator can only be used within an async lambda expression. Consider marking this lambda expression with the 'async' modifier.

Here is my code:

private static async Task GenerateGpsPointsForTruckAsync(int counter, CloudTableClient tableClient)
    {
        // Create a dummy VIN
        string vin = counter.ToString("D17");

        Random random = new Random();

        DateTime start = new DateTime(2010, 1, 1);
        int range = (DateTime.Today - start).Milliseconds;

        // Create the batch operation.
        TableBatchOperation batchOperation = new TableBatchOperation();

        // Prepare 10 batches of 100 GPS points
        Parallel.For(0, 10, i =>
        {
            for (int j = 0; j < 99; j++)
            {
                Location location = new Location(vin, start.AddDays(random.Next(range)).ToString());

                location.Coordinates = new GeoCoordinate(random.Next(30, 45), random.Next(75, 100));

                batchOperation.Insert(location);
            }

            await Task.Run(async () => 
            { 
                await LoadGpsPointsForTruckAsync(tableClient, batchOperation); 
            });
        });
    }

I looked at a few solutions on Stack Overflow but they don't seem to be working for me.

Upvotes: 1

Views: 3579

Answers (2)

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149628

Parallel.For is expecting you to mark its lambda as async as well, because of your outter await Task.Run:

Parallel.For(0, 10, async () => /* code */)

But, async-await doesn't play well with Parallel as it converts it's lambdas to async void via an Action delegate.

It seems that LoadGpsPointsForTruckAsync by itself is async, you can save yourself the parallel loop:

var coordinateTasks = Enumerable.Range(0, 100).Select(_ => 
            {
                Location location = new Location(vin, 
                                    start.AddDays(random.Next(range))
                                    .ToString())
                {
                   Coordinates = new GeoCoordinate(random.Next(30, 45),
                                                   random.Next(75, 100));
                }

                batchOperation.Insert(location);

                return LoadGpsPointsForTruckAsync(tableClient, batchOperation); 
            }

await Task.WhenAll(coordinateTasks);

Upvotes: 0

i3arnon
i3arnon

Reputation: 116636

You can't use async-await inside Parallel.For as it's older than async and doesn't await it (you probably don't want to either).

If you want to create multiple tasks that run on the thread pool you can do that with multiple calls to Task.Run with Task.WhenAll so you can await for all of them to complete:

await Task.WhenAll(Enumerable.Range(0,10).Select(i => Task.Run(() => Foo(i))));

That's appropriate when the operation is synchronous. If it's asynchronous you shouldn't offload it to the thread pool unless you have a reason to do so. You can simply call the method and await the results:

await Task.WhenAll(Enumerable.Range(0,10).Select(i => FooAsync(i)));

Upvotes: 3

Related Questions