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?
I would change to
await Task.WhenAll(tasks)
.– dcastro
If the context in which the code is inserted accepts
await
, then it is better. I will edit to reflect this possibility. Thanks!– Miguel Angelo
I assumed so, due to this comment: "in addition to locking every form". I think this code makes part of an Event Handler.
– dcastro
It’s true... I understood (I don’t know why) the code was inside a webservice, which would in turn consult other external webservices. =)
– Miguel Angelo
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 => {
 lock (ds)
 {
 ds.Merge(t.Result);
 }
 }));
 await Task.WhenAll(tasks);
– iuristona
I used two options besides your code, the Continuewith that puts processing next to task and lock because of multithread
– iuristona
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.
– Miguel Angelo
In the first example, it should not be
ToArray
? I can’t find anyWaitAll(List<Task>)
(onlyWaitAll(Task[])
)– luiscubal
@luiscubal: in both cases it was to be
ToArray
... habit force useToList
. Corrected. Thank you!– Miguel Angelo
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.– dcastro
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
@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.– Miguel Angelo
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
@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.
– Miguel Angelo
@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.
– iuristona
@Miguelangelo, the question has the label
winforms
whereforeWaitAll
is very bad. You should completely remove that option from your answer.– Paulo Morgado
From Framework 4.5 the method
WaitAny
has an overload that receives aIEnumerable<Task<TResult>>
. Of course internally it will create the array and call the overload that receives the array.– Paulo Morgado