I want to know when some parallel tasks are completed.
I’m using this code to make between 1500 and 2000 small WebClient.DownloadString with a 10 seconds HttpRequest Timeout on a website:
Task.Factory.StartNew(() =>
Parallel.ForEach<string>(myKeywords, new ParallelOptions
{ MaxDegreeOfParallelism = 5 }, getKey));
Sometimes, a query fails, so that there are exceptions and the function never finish, and the UI refresh inside each getKey function sometimes seems to be called twice, so I cannot get an accurate idea about how many tasks are completed. I’m calculating: Number of UI refresh calls / total number of keywords, and get a result between 100% and 250%, and I never know when task are completed. I search in a lot of SO discussion but none was a direct method or a method that suits my needs. So I guess Framework 4.0 doesn’t provides any Tasks.AllCompleted Event Handler or similar workaround?
Should I run my Parallel.Foreach in one other thread instead of my UI thread then add it?
myTasks.WaitAll
[EDIT]
A temporary solution was to copy my list of string in a ArrayList, then removing one by one each item from the list at the beginning of each query. Whenever the function worked well or not, I know when all items have been processed.
Parallel.ForEachis no different than other loops when it comes to handling exceptions. If an exception is thrown, then it is going to stop processing of the loop. This is probably why you’re seeing variances in the percentages (I assume you might be processing the count as you’re processing the loop).Also, you don’t really need
Parallel.ForEachbecuase the asynchronous calls that you’re making on theWebClientclass are going to block waiting on IO completion (the network responses), they are not computationally bound (Parallel.ForEachis much better when you are computationally bound).That said, you should first translate your calls to
WebClientto useTask<TResult>. Translating the event-based asynchronous pattern to the task-based asynchronous pattern is simple with the use of theTaskCompletionSource<TResult>class.Assuming that you have a sequence of
Uriinstances that are produced as a result of your calls togetKey, you can create a function to do this:Note, the above can be optimized to use one
WebClient, that is left as an exercise for you (assuming your tests show you need it).From there, you can get a sequence of
Task<string>:Note that you must call the
ToArrayextension method in order for the tasks to start running. This is to get around deferred execution. You don’t have to callToArray, but you must call something which will enumerate through the entire list and cause the tasks to start running.Once you have these
Task<string>instances, you can wait on them all to complete by calling theContinueWhenAll<TAntecedentResult>method on theTaskFactoryclass, like so:When this is done, you can cycle through the
tasksarray and look at theExceptionand/orResultproperties to check to see what the exception or result was.If you are updating a user interface, then you should look at intercepting the call to Enumerable.Select, namely, you should call the
ContinueWith<TNewResult>method on theTask<TResult>to perform an operation when that download is complete, like so:This will allow you to update things as they happen. Note that in the above case, if you call
Selectagain, you might want to check the state oft2and fire some other events, depending on what you want your error handling mechanism to be.