Knows Not Much
Knows Not Much

Reputation: 31546

Starting tasks in a for loop and then doing waitall on all of them

I saw couple of posts on this... but most people are manually creating tasks rather than receiving it from an API.

I have this code

        Task[] tasks = new Task[companyCount];
        // start all threads
        for(int i = 0; i < companyCount; i++) {
            string input = GetNextCompanyRecord(i);
            string token = GetUserToken();
            HttpClient client = GetHttpClient();
            StringContent content = CreateStringContent(input);
            var x = client.PostAsync(companyUrlString, content).ConfigureAwait(continueOnCapturedContext: false);
            tasks[i] = x;
        }
        Task.WaitAll(tasks);

the objective is that I start the task for each record in a for loop and then when all tasks are started ... doing a single waitall.

problem is that I cannot assign the x variable to the task array.

The PostAsync method is not returning me the Task. instead it is returning ConfiguredTaskAwaitable. But my hope was that I will get Task and I will be able to add that to my array and then do a waitall.

Upvotes: 0

Views: 1516

Answers (2)

Enigmativity
Enigmativity

Reputation: 117027

Just do it like this:

Task<HttpResponseMessage>[] tasks = new Task<HttpResponseMessage>[companyCount];
for(int i = 0; i < companyCount; i++)
{
    string input = GetNextCompanyRecord(i);
    string token = GetUserToken();
    HttpClient client = GetHttpClient();
    StringContent content = CreateStringContent(input);
    var x = client.PostAsync(companyUrlString, content);
    tasks[i] = x;
}
Task.WaitAll(tasks);

But, perhaps a cleaner way would be to do this:

var tasks =
    from i in Enumerable.Range(0, companyCount)
    let input = GetNextCompanyRecord(i)
    let token = GetUserToken()
    let client = GetHttpClient()
    let content = CreateStringContent(input)
    select client.PostAsync(companyUrlString, content);

Task.WaitAll(tasks.ToArray());

Or even better yet:

using (var client = GetHttpClient())
{
    var tasks =
        from i in Enumerable.Range(0, companyCount)
        let input = GetNextCompanyRecord(i)
        let token = GetUserToken()
        let content = CreateStringContent(input)
        select client.PostAsync(companyUrlString, content);

    Task.WaitAll(tasks.ToArray());
}

Upvotes: 6

dcastro
dcastro

Reputation: 68640

PostAsync is returning a Task (a Task<HttpResponseMessage>, to be precise), but then you're calling ConfigureAwait on it, which returns a ConfiguredTaskAwaitable.

Try getting a reference to the task first, and then calling ConfigureAwait.

var task = client.PostAsync(companyUrlString, content);
ConfiguredTaskAwaitable awaitable = task.ConfigureAwait(continueOnCapturedContext: false);
tasks[i] = task;

Also, consider:

  • removing the call to ConfigureAwait, since you don't seem to need it anyway.
  • changing your array from Task[] to Task<HttpResponseMessage>[], if you want to retrieve the tasks' results when they're done.

Upvotes: 3

Related Questions