مانبندی رشتهها (Thread
Scheduling) به مکانیزمی گفته میشود که نحوهی تخصیص زمان پردازنده به
ریسههای مختلف را تعیین میکند. در الگوریتمهای موازی C++17، کتابخانهی
استاندارد با ارائهی سیاستهای اجرایی par و par_unseq،
مدلهای زمانبندی متفاوتی را پشتیبانی میکند که هرکدام بر اساس سطح
موازیسازی و برداریسازی مورد انتظار، رفتار خاصی از خود نشان میدهند.
مدل زمانبندی در سیاست par
در سیاست std::execution::par،
کتابخانه مجاز است تا عناصر ورودی را بین ریسههای مختلف توزیع کند. توابع
عنصری میتوانند در ریسهی فراخواننده یا در ریسههای ایجادشده توسط
کتابخانه اجرا شوند. مدل زمانبندی در این حالت، از ضمانت پیشرفت موازی
(Parallel Forward Progress) پیروی میکند؛ به این معنا که اگر یک ریسه یک
قدم بردارد، در نهایت قدم دیگری نیز خواهد برداشت. این ویژگی به ریسهها
اجازه میدهد تا وارد بخشهای بحرانی شده و قفلها را بهدرستی مدیریت
کنند، زیرا ریسهای که قفل را در اختیار دارد، در نهایت دوباره زمان
پردازنده دریافت کرده و میتواند آن را آزاد کند.
مدل زمانبندی در سیاست par_unseq
سیاست std::execution::par_unseq سطح بالاتری از انعطافپذیری را برای زمانبند فراهم میکند. در این حالت، علاوه بر موازیسازی، اجازهی بردارگونهسازی و حتی مهاجرت بین ریسهها (مانند زمانبندی مبتنی بر parent-stealing) نیز وجود دارد. توابع عنصری میتوانند بهصورت نامرتب در ریسههای نامشخص اجرا شوند و درون هر ریسه نیز نسبت به یکدیگر غیردنبالهدار هستند. ضمانت پیشرفت در این حالت ضعیفتر
است و تنها تضمین میکند که یکی از ریسههایی که قدم برداشته، در نهایت
قدم دیگری بردارد؛ این موضوع به ریسهها اجازهی ورود به بخشهای بحرانی یا
گرفتن قفل را نمیدهد.
پیادهسازی زمانبند و استخر ریسهها
اگرچه
استاندارد C++17 مشخصات دقیق زمانبندی را تعیین نمیکند، اما
پیادهسازیهای مختلف از مکانیزمهای متنوعی استفاده میکنند. برخی
پیادهسازیها مانند poolSTL از یک
استخر ریسهی (Thread Pool) قابلکنترل پشتیبانی میکنند که به برنامهنویس
اجازه میدهد تا تعداد ریسهها را بهصورت دستی تنظیم کند. همچنین امکان انتخاب بین اجرای موازی و ترتیبی در زمان اجرا (Runtime) از طریق سیاستهای شرطی مانند par_if وجود دارد که برای مجموعهدادههای کوچک که سربار موازیسازی از مزایای آن بیشتر است، بسیار مفید واقع میشود.
محدودیتهای توابع عنصری در مدلهای زمانبندی
مدل زمانبندی تأثیر مستقیمی بر روی محدودیتهای توابع عنصری دارد. در سیاست par_unseq، بهدلیل امکان بردارگونهسازی و درهمآمیختگی اجرا، توابع عنصری نمیتوانند از عملیاتهای ناایمن برای بردارگونهسازی استفاده کنند، مانند تخصیص حافظه، گرفتن mutex یا استفاده از std::atomic. در حالی که در سیاست par، بهدلیل ضمانت پیشرفت قویتر، استفاده از این ابزارهای همگامسازی با رعایت احتیاط مجاز است.
مسئولیت برنامهنویس در برابر زمانبندی
صرفنظر از مدل زمانبندی، مسئولیت اصلی جلوگیری از رخدادهای رقابتی و بنبستها بر عهدهی برنامهنویس است. برای مثال، افزودن عناصر به یک std::vector مشترک از درون یک حلقهی موازی for_each، یک رخداد رقابتی است که حتی با مدل زمانبندی par نیز ایمن نیست. همچنین در صورت پرتاب شدن استثنا از درون توابع عنصری در هر سه سیاست، std::terminate فراخوانی میشود.
کلیدواژه ها : زمانبندی ریسه در C++17-مدل اجرای موازی-ضمانت پیشرفت موازی-استخر ریسه در C++-std::execution::par-std::execution::par_unseq-thread scheduling C++17-parallel forward progress-weakly parallel forward progress-parent stealing scheduler-C++17 parallel algorithms-مدیریت ریسه در STL-اجرای همزمان الگوریتمها-پیادهسازی زمانبند C++17-poolSTL thread pool-محدودیتهای par_unseq-مسئولیت برنامهنویس در موازیسازی