هندل کردن خطاها در حلقه های موازی
در حالت عادی در حلقه های ترتیبی بعد از بوجود اومدن یک خطا (Exception) ادامه حلقه متوقف میشود ولی در حلقه های موازی بعد از بوجود اومدن خطا Thread های در حال اجرا اجازه دارن تا تکمیل کارشون اجرا بشن ولی مابقی تکرار های حلقه دیگه اجرا نمیشن.
با توجه به اینکه چندین Thread همزمان در حال اجرا هستن احتمال اینکه چندین خطا (Exception) داشته باشیم زیاده از اینرو بجای کلاس Exception با کلاس AggregateException سرو کار داریم که یکی از خواصیت های (Property) اون InnerExceptions هست که شامل کلکسیونی از خطا ایجاد شده طی ه حلقه ماست.
در مواجه با خطا معمولا 2 حالت پیش میاد
1-متوقف شدن حلقه در صورت ایجاد خطا
2-ادامه حلقه در صورت ایجاد خطا
حالت اول بصورت پیش فرض اعمال میشه (البته همونطور که در ابتدا گفتم این توقف آنی نیست)
using System; using System.Linq; using System.Threading.Tasks; using System.Collections.Concurrent; namespace Parallel_TesTer { internal class Program { private static void Main(string[] args) { try { Parallel.ForEach(Enumerable.Range(1, 100), (int i, ParallelLoopState state, long index) => { Console.WriteLine("Index {0}, result {1}", index, 100 / (i / 2)); if (state.IsExceptional) { state.Stop(); } }); } catch (AggregateException aggEx) { foreach (var innerEx in aggEx.InnerExceptions) { Console.WriteLine(innerEx.Message); } } Console.ReadLine(); } } }
یک وهله از کلاس ParallelLoopState بصورت پیش فرض بوسیله کلاس Parallel برای هر حلقه ایجاد میشه; که از این کلاس میشه برای اگاهی از دیگر تکرار ها ی حلقه و تعامل با اونا استفاده کرد.
این کلاس 2 تا متد مهم بنام Stop و Break داره که تو شرایط خاصی میشه از اونا استفاده کرد مثلا اگر در دیگر تکرار ها خطایی رخ داده بود با برسی خاصیت IsExceptional این کلاس.
متد Stop:در مناسبترین حالت سیستم ادامه تکرار ها رو متوقف میکنه
متد Break:بعد از فراخوانی این متد تمامی دورهای کوچکتر از دور جاری اجرا میشن براساس ایندکس مثلا اگه در دور 20وم از حلقه بالا این متد صدا زده بشه حتما 1 تا 19 اجرا میشن و خاصیت LowestBreakIteration کلاس ParallelLoopState برابر مقدار 20 میشه و با استفاده از این مقدار میتونیم متوجه بشیم تا کجای دیتا سورس ما پردازش شده.
در هر دو متد بالا معمولا نتایج دقیق نیست و این Trade-Off استفاده از حلقه های موازی و داشتن بهره وری بالاس.
و اما حالت دوم که من بهش نیاز داشتم ادامه دادن بعد از بروز خطاس
با توجه به طبیعت همزمانی از کلکسیون ConcurrentQueue که lock-free و thread-safe هست استفاده میکنیم در آخر هم در صورت وجود خطا لیست خطا ها رو throw میکنیم.
private static void DoParallel() { var exceptions = new ConcurrentQueue(); Parallel.ForEach(Enumerable.Range(1, 100), (int i) => { try { Console.WriteLine("Index {0}, Result {1}", i, 100/(i/2)); } catch (Exception ex) { exceptions.Enqueue(ex); } }); if (!exceptions.IsEmpty) throw new AggregateException(exceptions); }
نظرات
ارسال یک نظر