اشتراکها
TPL Dataflow (Task Parallel Library Dataflow) is a .NET Framework library designed for building robust and scalable concurrent data processing pipelines. It offers a declarative model in which you define a network of interconnected "blocks" that process and transport data, enabling efficient and flexible parallelism.
اشتراکها
مقدمه ای بر برنامه نویسی همزمان
What is concurrent programing? Simply described, it’s when you are doing more than one thing at the same time. Not to be confused with parallelism, concurrency is when multiple sequences of operations are run in overlapping periods of time. In the realm of programming, concurrency is a pretty complex subject. Dealing with constructs such as threads and locks and avoiding issues like race conditions and deadlocks can be quite cumbersome, making concurrent programs difficult to write. Through concurrency, programs can be designed as independent processes working together in a specific composition. Such a structure may or may not be made parallel; however, achieving such a structure in your program offers numerous advantages.
بازخوردهای دوره
اعمال غیر همزمان و چند ریسمانی
مطلب تکمیلی دوم ،
ویدیو فارسی بسیار مفید در رابطه با مباحث TPL ( نخها ، استخر نخها ، چند نخی ، موازی ، متقارن ، نامتقارن و.... )
آیا تا به حال لیستی از دیتا داشتهاید که بخواهید بر روی آنها کاری را انجام دهید؟ مثلا لیستی از مشتریان که باید برای تک تک آنها Pdf ای را بسازید، یا لیستی از مشتریان که باید برای تک تک آنها بیمه نامه صادر کنید، یا مثلا لیست اطلاعات بلیطهای قابل فروش را گرفتهاید و برای تک تک آنها میخواهید کمیسیون حساب کنید و ...
در اکثر مواقعی کاری که برای تک تک آیتمها قرار است انجام شود، ساده است و با استفاده از یک حلقه foreach کار تمام میشود. اما در بعضی مواقع کار زمانبر است؛ حال یا به علت وجود کاری CPU bound مثل درست کردن Pdf و محاسبات، یا کار IO Bound است مثل ارسال یک HTTP Request به ازای هر مشتری، یا ذخیره کردن چیزی در دیتابیس که هم CPU bound است و هم IO bound و ترکیبی از مواردی که گفتیم را دارد.
فرض کنیم صد مشتری داریم و برای تک تک آنها میخواهیم کاری انجام دهیم. اگر از یک foreach ساده استفاده کنیم و هر عمل یک ثانیه طول بکشد، کل روال 100 ثانیه طول میکشد که جالب نیست.
public async Task Sample() { var customers = await GetCustomersFromSomeWhere(); foreach (var customer in customers) { await DoSomethingWithCustomer(customer); } }
با اندکی جستجو در اینترنت به Task.WhenAll میرسیم و مشکلی که دارد این است که هر 100 کار را با هم شروع میکند که میتواند اثرات مخربی روی کلیت عملکرد سرور بگذارد:
public async Task Sample() { var customers = await GetCustomersFromSomeWhere(); await Task.WhenAll(customers.Select(c => DoSomethingWithCustomer(c))); }
public async Task Sample() { var customers = await GetCustomersFromSomeWhere(); await customers.Select(c => Observable.FromAsync(() => DoSomethingWithCustomer(c))).Merge(maxConcurrent: 25); }
استفاده از Rx وقتی که دستورات داخل DoSomethingWithCustomer به صورت IO bound باشند (اتصال به دیتابیس و ارسال Http Request و ...) به خوبی جواب میدهد. ولی اگر دستورات داخل DoSomethingWithCustomer به صورت CPU bound باشند، مثل محاسبات یا ساختن Pdf و ... دیگر این روش جواب نمیدهد و اینجاست که باید از Task Parallel Library استفاده کنیم ( البته Task Parallel Libraray یا به اختصار TPL هم برای IO Bound و هم برای CPU Bound مناسب است).
برای استفاده از TPL داریم:
public async Task Sample() { var customers = await GetCustomersFromSomeWhere(); ActionBlock<Customer> action = new ActionBlock<Customer>(c => DoSomethingWithCustomer(c), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 25 }); foreach (var customer in customers) { action.Post(customer); } action.Complete(); await action.Completion; }
همانطور که میبینید، بحث 25 تا 25 تا اجرا کردن در اینجا نیز وجود دارد، با این تفاوت که بسیار هوشمندانهتر کارها را به صورتی پیش میبرد که از منابع سرور به بهینهترین شکل ممکن استفاده شود و همین TPL را هم برای اعمال IO bound و هم اعمال CPU bound مناسب میکند.
گزینههایی از قدیم نیز وجود دارند، مانند استفاده از Thread و Semaphore و ... که ابدا استفاده مستقیم از آنها توصیه نمیشود.
البته با TPL و RX میشود کارهای خیلی بیشتری نیز انجام داد و این دو فقط برای این سناریو ساخته نشدهاند و همه جا جایگزین یکدیگر نیستند و هر دو دنیای وسیعی هستند که توصیه میکنم به هر دو نگاهی بیاندازید. همچنین TPL تا جایی که میدانم جزو مواردی است که در بیرون از دنیای NET. وجود ندارد و یکی از ارزشمندترین ویژگیهای Unique در NET. است که به این سادگی چنین مسئلهای با این کیفیت حل شود.
توجه داشته باشید که اگر فرآیندی که برای تک تک Customerها در مثال فوق قرار است انجام شود، خود یک روال سنگین و زمان بر باشد، بهتر است از روشهای دیگری مبتنی بر Event processing و ابزارهایی چون Azure Service Bus یا Mass Transit استفاده کنیم که کمک میکنند اگر مثلا سه سرور داریم، بار پردازش آن 100 مشتری مثال ما، بین سه سرور هم پخش شود که این مورد پیچیدگیهای خود را دارد و در اینجا که فرض بر این است که سناریو خیلی پیچیده و میزان بار خیلی زیاد نیست و همچنین نیازی هم به استفاده از این موارد و اضافه کردن پیچیدگیهای بیشتر به برنامه نیست. در واقع TPL علیرغم کار بسیار ارزشمندی که میکند، در نهایت یک Nuget Package است که در یک دستگاه موبایل Android و با Xamarin نیز قابل استفاده است.
البته این همه داستان نیست. برای مثال در صورتی که فرآیندی بخواهد به صورت Concurrent / Parallel انجام شود و در انجام آن از Entity Framework Db Context استفاده شده باشد، کد به مشکل بر میخورد و خطا میدهد، چون یک Instance از DbContext مناسب انجام چند کار همزمان نیست. به واقع تمامی Objectهایی که Thread Safe نباشند، در روشهای فوق به مشکل بر میخوردند. همچنین بحث مدیریت کردن Transaction در صورتی که بخواهید با دیتابیس هم کار کنید نیز خود مسئلهای است که باید حل شود.
حل مسائلی که گفته شد و ادغام کردن روشهای فوق با بحث Dependency Injection و ... موضوع بحث قسمت بعدی این مطلب است.
اشتراکها
ReSharper Ultimate 2020.1 منتشر شد
اشتراکها
برنامه نویسی چند هسته ای در .NET
اشتراکها