Loop asynchronous calls and process data at the end of all runs

Asked

Viewed 737 times

3

I have a method of web service which receives the IP of a server as a parameter and returns a DataSet. Several servers must be consulted by this web service, Um List<string> contains the list of Ips. A loop executes the method, a merge of the return to a local Dataset and then a DataGridView winforms is populated with these data.

The code that does this is simple:

    foreach (var server in servers)
    {
        ds.Merge(ws.GetData(server));
    }

    dataGridView1.DataSource = ds.Tables[0];

This call is run in a queue and takes a while to finish, in addition to locking every form.

How can I convert this process to an asynchronous form using the method GetDataAsync with async and Task to run all calls at once and display the data in the DataGridView at the end?

1 answer

2


Do so:

var tasks = servers.Select(server => ws.GetDataAsync(server)).ToArray();
Task.WaitAll(tasks);
var results = tasks.SelectMany(t => t.Result).ToList();
foreach (var res in results)
    ds.Merge(res);

If the method in which the code exists (e.g. a windows Forms control event) can be declared with async, could still use await and let the whole block run after await for when all results are ready:

private async Task EventoDoForm(EventArgs e)
{
    var tasks = servers.Select(server => ws.GetDataAsync(server)).ToArray();
    await Task.WhenAll(tasks);
    var results = tasks.SelectMany(t => t.Result).ToList();
    foreach (var res in results)
        ds.Merge(res);
}

So the event will not crash the form.

  • 3

    I would change to await Task.WhenAll(tasks).

  • If the context in which the code is inserted accepts await, then it is better. I will edit to reflect this possibility. Thanks!

  • I assumed so, due to this comment: "in addition to locking every form". I think this code makes part of an Event Handler.

  • It’s true... I understood (I don’t know why) the code was inside a webservice, which would in turn consult other external webservices. =)

  • Yes, the idea is to use the form event call with async, I followed the idea and it went like this: var tasks = servers.Select(server => ws.GetAllMKTAsync(server).ContinueWith(t => {&#xA; lock (ds)&#xA; {&#xA; ds.Merge(t.Result);&#xA; }&#xA; }));&#xA; await Task.WhenAll(tasks);

  • I used two options besides your code, the Continuewith that puts processing next to task and lock because of multithread

  • Don’t worry about multithreading in the context of windows Forms when using Tasks, because windows Forms defines a Scheduler that runs in sequence, all within the main thread, within the application loop.

  • In the first example, it should not be ToArray? I can’t find any WaitAll(List<Task>) (only WaitAll(Task[]))

  • @luiscubal: in both cases it was to be ToArray... habit force use ToList. Corrected. Thank you!

  • Michael, it is possible (and likely) that the continuation tasks run in parallel.. Assuming the structure ds not synchronized, lock in @iuristona comment is required.

  • There is no guarantee that the continuation tasks will be executed in the UI thread. However, you can get this guarantee by going through TaskScheduler.FromCurrentSynchronizationContext() for the Continuewith method, in that case the lock would not be necessary. I think this method would have better performance.

  • @dcastro: you’re right... I was thinking of the example using await. In this case, the continuation (everything that comes after await) will run in the context of windows-Forms sync.

  • I made the attempt with TaskScheduler.FromCurrentSynchronizationContext() and without the lock, I had no noticeable difference, but I will keep. I currently have 9 servers on the list. The process went from ~24 seconds to ~14 seconds. I expected it to get even faster, with each call taking on average 2.5s.

  • @iuristona: In this case, the delay will be greater or equal to the longest time between calls. That is, it will have no relation with the average, but with the worst case between the webservices.

  • @Miguelangelo yes, that’s what I expected. The worst call takes 6 seconds. Apparently my web server is not able to run all the calls in parallel. I will check this later. But the client code was OK.

  • @Miguelangelo, the question has the label winforms wherefore WaitAll is very bad. You should completely remove that option from your answer.

  • From Framework 4.5 the method WaitAnyhas an overload that receives a IEnumerable<Task<TResult>>. Of course internally it will create the array and call the overload that receives the array.

Show 12 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.