Parallel.ForEach
չի տարբերվում մյուս օղակներից, երբ խոսքը վերաբերում է բացառությունների մշակում: Եթե բացառություն արվի, ապա այն պատրաստվում է դադարեցնել օղակի մշակումը: Հավանաբար սա է պատճառը, որ դուք տեսնում եք տոկոսների տարբերություններ (ես ենթադրում եմ, որ դուք կարող եք մշակել հաշվարկը, երբ մշակում եք հանգույցը):
Բացի այդ, ձեզ իրականում պետք չէ Parallel.ForEach
ա> քանի որ ասինխրոն զանգերը, որոնք դուք կատարում եք WebClient
դասում պատրաստվում է արգելափակել սպասումը IO-ի ավարտը (ցանցի պատասխանները), դրանք հաշվողականորեն կապված չեն (Parallel.ForEach
շատ ավելի լավ է, երբ դուք հաշվողականորեն կապված եք):
Այսպիսով, դուք պետք է նախ թարգմանեք ձեր զանգերը WebClient
՝ Task<TResult>
օգտագործելու համար: իրադարձությունների վրա հիմնված ասինխրոն օրինակը թարգմանվում է առաջադրանքի վրա հիմնված ասինխրոն օրինաչափություն պարզ է TaskCompletionSource<TResult>
դաս:
Ենթադրելով, որ դուք ունեք Uri
օրինակների հաջորդականություն, որոնք արտադրվում են որպես getKey
-ին կատարած ձեր զանգերի արդյունքում դուք կարող եք ստեղծել գործառույթ՝ դա անելու համար.
static Task<String> DownloadStringAsync(Uri uri)
{
// Create a WebClient
var wc = new WebClient();
// Set up your web client.
// Create the TaskCompletionSource.
var tcs = new TaskCompletionSource<string>();
// Set the event handler on the web client.
wc.DownloadStringCompleted += (s, e) => {
// Dispose of the WebClient when done.
using (wc)
{
// Set the task completion source based on the
// event.
if (e.Cancelled)
{
// Set cancellation.
tcs.SetCancelled();
return;
}
// Exception?
if (e.Error != null)
{
// Set exception.
tcs.SetException(e.Error);
return;
}
// Set result.
tcs.SetResult(e.Result);
};
// Return the task.
return tcs.Task;
};
Նկատի ունեցեք, որ վերը նշվածը կարող է օպտիմիզացվել մեկ WebClient
-ն օգտագործելու համար, որը մնում է ձեզ համար որպես վարժություն (ենթադրելով, որ ձեր թեստերը ցույց են տալիս, որ դրա կարիքն ունեք):
Այնտեղից կարող եք ստանալ Task<string>
-ի հաջորդականությունը.
// Gotten from myKeywords
IEnumerable<Uri> uris = ...;
// The tasks.
Task<string>[] tasks = uris.Select(DownloadStringAsync).ToArray();
Նկատի ունեցեք, որ դուք պետք է զանգահարեք ToArray
ընդլայնման եղանակով որպեսզի առաջադրանքները սկսեն գործել: Սա նախատեսված է հետաձգված կատարումը շրջանցելու համար . Պետք չէ զանգահարել ToArray
ին, բայց դուք պետք է զանգահարեք մի բան, որը կթվարկի ամբողջ ցանկը և կառաջարկի առաջադրանքների գործարկումը:
Երբ դուք ունեք այս Task<string>
օրինակները, կարող եք սպասել, որ դրանք բոլորը ավարտվեն՝ զանգահարելով ContinueWhenAll<TAntecedentResult>
մեթոդ TaskFactory
դասում , այսպես.
Task.Factory.ContinueWhenAll(tasks, a => { }).Wait();
Երբ դա արվի, դուք կարող եք շրջել tasks
զանգվածի միջով և դիտել Exception
և/կամ Result
հատկություններ ստուգելու համար, թե որն է բացառությունը կամ արդյունքը:
Եթե դուք թարմացնում եք օգտատիրոջ միջերեսը, ապա պետք է ուշադրություն դարձնեք զանգի գաղտնալսմանը Հաշվառելի: Ընտրեք, մասնավորապես, դուք պետք է զանգահարեք ContinueWith<TNewResult>
մեթոդը: the Task<TResult>
գործողություն կատարելու համար երբ ներբեռնումն ավարտված է, օրինակ՝
// The tasks.
Task<string>[] tasks = uris.
Select(DownloadStringAsync).
// Select receives a Task<T> here, continue that.
Select(t => t.ContinueWith(t2 => {
// Do something here:
// - increment a count
// - fire an event
// - update the UI
// Note that you have to take care of synchronization here, so
// make sure to synchronize access to a count, or serialize calls
// to the UI thread appropriately with a SynchronizationContext.
...
// Return the result, this ensures that you'll have a Task<string>
// waiting.
return t2;
})).
ToArray();
Սա թույլ կտա ձեզ թարմացնել բաները, երբ դրանք տեղի են ունենում: Նկատի ունեցեք, որ վերը նշված դեպքում, եթե նորից զանգահարեք Select
-ին, գուցե ցանկանաք ստուգել t2
-ի վիճակը և ակտիվացնել որոշ այլ իրադարձություններ՝ կախված նրանից, թե ինչպիսին է ձեր սխալների հետ աշխատելու մեխանիզմը:
09.10.2012