هر آنچه درمورد چند رشته خواندن باید بدانید: سازگاری انتشار واقع گرایانه [قسمت 2]

عکس پروفایل نویسنده <> x20 xeil <20> il / h3>

Xoogler ، Ph.d در سیستم های رایانه ای. تازه کار در منطقه خلیج سانفرانسیسکو.

در آخرین قسمت ما ، سازگاری متوالی ، قرارداد برنامه نویسی رشته ای بین برنامه نویسان و سیستم های رایانه ای را معرفی کردیم.

ما در شرف عمیق ترین مدل قوام حافظه در عمل هستیم ، سازگاری انتشار . زیبایی این سازگاری این است که اگرچه پیاده سازی برای سیستم ها بسیار ارزان تر است ، اما با توجه به قفل شدن مناسب ، ما می توانیم به طور رسمی ثابت کنیم که معادل سازگاری متوالی است.

پس از اتمام این مقاله ، مستندات سفارش + Memory_ord C ++ C در واقع به نظر شما انگلیسی است. همچنین بدون استفاده از راه حل های خارج از قفسه مانند قفل های ارائه شده توسط کتابخانه ، راحت تر می توانید در آب کار کنید و روش های همگام سازی خود را دنبال کنید.

اما اول ، لازم است یک دور زدن برای با برنامه اصلی چند رشته ای آشنا شوید: قفل ها ، که برای آزاد کردن شهود و پیاده سازی سازگاری بسیار ضروری است.

قفل

استفاده از حروف < / strong>

در آخرین قسمت خود ، مثالی را پوشش دادیم که در آن دو رشته برای به روزرسانی دو متغیر مسابقه می دهند i و j. بیایید از آن به عنوان مثال 1 خود در این مقاله استفاده مجدد کنیم:

int i = 0 ؛
int j = 0 ؛
int خروجی [ 2 void func0 () { i = 1 ؛ خروجی [ 1 ] = j؛
} void func1 () { j = 1 ؛ خروجی [ 0 ] = i؛
}

صادقانه بگویم ، این فقط یک نمونه ساختگی کاملاً مناسب برای نشان دادن قوام حافظه شکسته بود. رایج ترین نیاز به همگام سازی در واقع از ضرورت گروه بندی چندین عملیات حافظه در یک عکس ناشی می شود. این نیاز به اندازه مفهوم چند رشته ای قدیمی بود. این حتی قبل از SMP یا مدل سازگاری است. بیایید به مثال 2 واقع بینانه تر نگاه کنیم.

int id = 0 ؛
int my_id [ 2 void func0 () {my_id [ 0 ] = شناسه ++ ؛ } void func1 () {my_id [ 1 ] = شناسه ++ ؛ }

در اینجا ما دو رشته داریم که می خواهند شناسه های منحصر به فرد خود را تولید کنند. خطری که در اینجا وجود دارد اینست که افزایش بی گناه id ++ در واقع دو عملیات حافظه است که با هم کوبیده می شوند. معمولاً از آن به عنوان خواندن-اصلاح-نوشتن یاد می شود. اگر این قطعه را در کامپایلر اکسپلورر جایگذاری کنیم ، می توانیم نشان دهیم که در x86 هر عملکرد تقریباً 4 دستورالعمل را می گیرد – خواندن-افزودن-نوشتن برای id ++ و اختصاص مقدار به my_id:

func0 (): # @ func0 () شناسه متحرک (٪ rip) ،٪ eax leal 1 (٪ rax) ،٪ ecx Movl٪ ecx ، id (٪ rip) movl٪ eax، my_id (٪ rip) retq

البته ، ما در اینجا عملکرد تابع را به عنوان یک دستورالعمل مناسب محاسبه نمی کنیم.

شرایط مسابقه مشهود است. اگر هر دو رشته ابتدا خواندن ها را قبل از اینكه دیگری كل توالی خواندن-اصلاح-نوشتن را انجام دهد ، انجام دهد ، برای هر دو رشته می توان شناسه 0. گرفت.

ما می توانیم همچنین یک برنامه کامل بنویسید که این نتیجه را ایجاد کند:

# شامل
# شامل int id = 0 ؛
int my_id [ 2 void func0 () {my_id [ 0 ] = شناسه ++ ؛ } void func1 () {my_id [ 1 ] = شناسه ++ ؛ } void run_test () { id = 0 ؛ std :: رشته t0(func0) ؛ std :: رشته t1(func1) ؛ t0 پیوستن () t1 پیوستن () std :: cout << my_id [ 0 ] << my_id [ 1 ] << << span> std
:: endl ؛
} int main () { برای (؛؛) { run_test ()؛ } بازگشت 0 ؛
}

آیا می بینید که ما اساساً با مسئله دیگری روبرو هستیم که مثال 1 در اینجا وجود دارد؟ مثال 1 می تواند خراب شود زیرا دستورالعمل ها می توانند دوباره مرتب شوند ، و در اینجا ما می شکنیم زیرا دستورالعمل ها ممکن است بهم پیوسته باشند ، حتی اگر کاملاً به ترتیب برنامه اجرا شوند!

One راه حل این مشکل این است که id را در یک ساختار اتمی std :: و ​​تبدیل آن را به API fetch_add خود تبدیل کنید ، که فقط یک دستورالعمل اتمی سخت افزاری ویژه ایجاد می کند که از تجارت-اصلاح-نوشتن نوشتن مراقبت می کند. اما این فقط یک میانبر است. قبل از استاندارد C ++ 11 ، ما فقط توالی خواندن-تغییر-نوشتن شناسه را در یک بخش مهم قدیمی و محافظت شده توسط قفل ها قرار می دادیم تا دسترسی به آن به طور متقابل از بین برود.

پیاده سازی Spinlock

قفل ها چگونه اجرا می شوند؟ به نظر می رسد ، قفل کردن فقط یک خواندن-تغییر-نوشتن به تنهایی است. بیایید نگاهی به ساده ترین نوع قفل – spinlock بیندازیم. در اینجا می توانیم با استفاده از C ++ ‘s

std :: atomic یک کار ساده انجام دهیم. در قدیم ، مونتاژ را خودمان تعبیه کرده بودیم.

class spinlock < / span> {
عمومی : void lock () { برای (؛؛) { bool انتظار_الو = نادرست ؛ اگر (مشغول.compare_exchange_strong (انتظار_پذیر ، درست )))) بازگشت ؛ } } void unlock () {busy.store ( false )؛ } خصوصی : std :: atomic < bool > busy = false ؛

این spinlock نامیده می شود زیرا این اکتساب مدام در حالت قفل می چرخد ​​به این امید که لحظه بعد بدون مسدود شدن شلوغ نباشد. حالت spinlock می تواند به اندازه 1 بیت باشد. کاوشگر کامپایلر نشان می دهد که بدست آوردن قفل دستورالعمل cmpxchg (ایستاده برای مقایسه و مبادله) را ایجاد می کند:

main: #main movb $ 0 ، -8 (٪ rsp) Movb $ 1 ،٪ cl
.LBB0_1: # => این عنوان داخلی حلقه: عمق = 1 xorl٪ eax ،٪ eax قفل کردن cmpxchgb٪ cl ، -8 (٪ rsp) jne .LBB0_1 movb $ 0 ، -8 (٪ rsp) xorl٪ eax ،٪ eax retq

این دستورالعمل به شرح زیر است: “مقدار به حالت قفل برمی گردد. این عملیات می تواند موفقیت آمیز باشد یا شکست بخورد ، و در صورت عدم موفقیت ما فقط تلاش خود را ادامه می دهیم تا در واقع قفل را بدست آوریم. اما به هر صورت ، 3 کار با هم و بدون وقفه انجام می شود. مجدداً بخوانید ، به روز کنید و بنویسید:

لازم نیست نسخه قفل دوباره امتحان شود ، و ما بدون قید و شرط مقدار “مشغول” را به حالت قفل می نویسیم.

در اینجا توجه داشته باشید می توانید وانمود کنید که در اینجا ثبات متوالی داریم. در حال حاضر ، شما باید به من اعتماد کنید که کد قفل در محیط های تک پردازنده و چند پردازنده کار خواهد کرد. پس از اتمام مقاله دلیل آن را خواهید دید.

اکنون اجازه دهید با استفاده از spinlock از متغیر id

محافظت کنیم و هر دو شناسه ما باید منحصر به فرد باشند.

# شامل <اتمی> باشد
# شامل شود
#mutex> را وارد کنید
# شامل <رشته> شود class spinlock {
عمومی: باطل قفل () { برای (؛؛) { bool used_val = false ؛ اگر (مشغول.compare_exchange_strong (انتظار_پذیر ، درست ))) بازگشت ؛ } } باطل باز کردن قفل () {busy.store ( false )؛ } خصوصی: std :: atomic مشغول = نادرست ؛
}؛ اسپینلاک متر
int id = 0 ؛
int my_id [ 2 باطل func0 () { std :: lock_guard g (m)؛ my_id [ 0 ] = شناسه ++؛
} void func1 () { std :: lock_guard g (m)؛ my_id [ 1 ] = شناسه ++؛
} void run_test () { id = 0 ؛ std :: thread t0 (func0)؛ std :: thread t1 (func1)؛ t0 پیوستن () t1 پیوستن () std :: cout << my_id [ 0 ] << my_id [ 1 ] << std :: endl؛ } int main () { برای (؛؛) { run_test ()؛ } بازگشت 0 ؛
}

سازگاری انتشار

سرانجام ، ما به دوره اصلی این قسمت رسیدیم. به یاد بیاورید که سازگاری متوالی ، در بیشتر موارد ، نیاز به یک ترتیب جهانی برای همه عملکردهای حافظه دارد. اجرای این امر در پردازنده های مدرن بسیار دشوار است. X86 TSO یک تلاش برای آرام کردن سفارشات دسترسی به حافظه است ، و در قسمت آخر می توانیم ببینیم که اگر دسترسی به متغیرهای مشترک به درستی مرتب نشود ، ثبات ترتیبی را از بین می برد.

افراد متوجه شده اند که بیشتر اوقات ، متغیرهای مشترک در واقع توسط قفل محافظت می شوند. همانطور که قبلاً اشاره کردیم ، این عادت قبل از چند پردازنده است ، بنابراین هیچ ارتباطی با کنار آمدن با مرتب سازی مجدد حافظه ندارد. اما از آنجا که قفل ها مکان های حافظه خاصی هستند ، فکر این است که به جای اجرای دستورات حافظه در همه جا ، شاید بتوانیم برای حفظ توهم قوام متوالی کار هوشمندانه ای را برای قفل ها انجام دهیم ، در حالی که اجازه می دهیم جمعیت عمومی دستورالعمل ها اسلاید شود؟

اینجاست که سازگاری انتشار (RC) ایجاد می شود. دو طعم RC ، RCsc و RCpc وجود دارد. هر دو برابر هستند. بیایید با RCsc شروع کنیم که درک آن آسان تر است اما در مقایسه با RCpc قابل اجرا نیست.

RCsc

این نکات RCsc را خلاصه می کند:

  • دسترسی به حافظه در الف) گزینه های همگام سازی ، به دست آوردن و آزاد کردن قفل ، و ب) عملیات عادی طبقه بندی می شود.
  • دسترسی به حافظه محلی امکان پذیر نیست مرتب سازی مجدد پس از انتشار یک نسخه.
  • قبل از دستیابی مجدد ، دسترسی به حافظه محلی مجدداً مرتب نمی شود.
  • هماهنگی سازی ها خود سازگاری متوالی را مشاهده می کنند ، از این رو زیرنویس “sc” در نام قوام است.
  • اینجا جایی است که می خواهم هزار کلمه برای توصیف مکانیسم بیان کنم. بنابراین در عوض بیایید یک عکس بیندازیم. این نشان می دهد که چرا این نقاط توهم قوام متوالی را حفظ می کنند.

    “(<) دارای کلاس ما

    ما دارای” مهم است <دارای ما ” اجرا بر روی دو پردازنده A و B ، به ترتیب CS-A و CS-B نامیده می شود. این موارد به صورت دو جعبه در نمودار بالا نشان داده شده است. بخشهای مهم توسط یک قفل محافظت می شوند. در داخل CS’es برخی از دسترسی ها به مکان های خصوصی پردازنده ها و برخی دیگر دسترسی به متغیرهای مشترک وجود دارد. به راحتی می توان اثبات کرد که تمام دسترسی ها در CS-A دقیقاً قبل از همه دسترسی ها در CS-B اتفاق می افتد و این در سطح جهانی مشاهده می شود. مهمترین ترفند ترتیب سازگاری پی در پی بین نسخه CS-A و کسب CS-B است که در قانون شماره 4 ایجاد شده است. یعنی آزاد سازی CS-A جایی که علامت” <"نشان دهنده رابطه جهانی اتفاقات قبل از آن است.

    از قانون شماره 2 و 3 ، هرگونه عملکرد در CS-A قبل از انتشار CS-A به پایان می رسد . همچنین هرگونه فعالیت در CS-B پس از کسب CS-B به پایان می رسد. بنابراین موارد زیر را داریم: Op در CS-A <نسخه CS-A و CS-B

    دسترسی خصوصی در اینجا بی ربط است ، زیرا در هر زمان توسط یک پردازنده انجام می شود و نظم جهانی توسط آن پردازنده تعریف می شود نظم ، که تضمین شده است برابر با ثبات متوالی است ، حتی اگر برخی از ترتیب بندی مجدد اتفاق بیفتد.

    همچنین رابطه قوی اتفاقات قبل از ما دسترسی های مشترک را در همان وضعیت قرار داده است. در هر زمان فقط یک پردازنده وجود دارد که دسترسی ها را به طور انحصاری انجام می دهد ، بنابراین نظمی که اجرا می کند به عنوان سفارش جهانی تعریف می شود. این سفارش باید معادل یک دستور قوام متوالی باشد ، که این قانون طلایی است که تاکنون هیچ سیستمی جرأت شکستن آن را ندارد.

    می بینید که تمام قوام انتشار در حال انجام بانک است ، این است که خارج بخشهای مهم نباید هیچ دسترسی مشترکی داشته باشند. اگر این کار را انجام دهید ، برای تعیین سفارش دسترسی به تنهایی خودتان هستید. به همین دلیل است که C ++ 11 از نوع دسترسی های غیرقانونی است که توسط قفل ها یا ساختارهای اتمی و مرتبط محافظت نمی شود. با بازگشت به مثال 1 در ابتدای کار ، به عنوان گزینه هایی برای استفاده از std :: atomic ، می توانیم به طور بالقوه از قفل مناسب برای رسیدن به همان هدف استفاده کنیم.

    RCpc

    ما فقط ثابت کردیم که تا زمانی که داده ها همیشه توسط قفل محافظت شوند ، RCsc معادل سازگاری متوالی خواهد بود. با این وجود ، با احتیاج بیش از حد مورد نیاز RCsc در اینجا ، اجرای آن برای پردازنده های واقعی بسیار دشوار است. 2 نوع اجرای سفارش را می طلبد: سازگاری متوالی بین گزینه های همگام سازی و ترتیب محلی بین گزینه های همگام سازی و عادی. به این فکر کنید که هنگام طراحی دستورالعمل ها چگونه آن رابط را تهیه می کنید. معماری های معمول فقط حصارهای حافظه محلی را به شما می دهند. به عنوان مثال ، x86 دارای یک دستورالعمل MFENCE است که از ترتیب عملیات حافظه محلی در آن دستورالعمل جلوگیری می کند.

    اکنون RCpc آمده و مانع نگرانی ما می شود. این می گوید سازگاری متوالی بین گزینه های همگام سازی ممکن است بیش از حد کافی باشد. ما می توانیم آن را با یک مدل سازگاری به نام سازگاری پردازنده جایگزین کنیم ، و جهان همچنان ادامه خواهد یافت.

    سازگاری پردازنده فرض می کند که هر پردازنده حافظه محلی خود را دارد (که یک فرض بسیار واقع گرایانه است ، از آنجا که معمولاً “هسته ها” حافظه نهان خصوصی L1 خاص خود را دارند).

    من عمداً از پیوند دادن تعریف ویکی پدیا از سازگاری پردازنده در اینجا خودداری کردم. اگر حتماً نگاه می کنید ، باید به شما هشدار دهم که این یکی از موارد نادر است که ویکی پدیا اشتباه است! به یکی از شکل های آن نگاه کنید:

    توضیح کلاس برای مثال “با توجه به پارامتر” “این پارامتر است” آنچه که این می گوید این است که پردازنده 1 مقدار ارزش 1 را برای مکان x صادر می کند. پردازنده 2 آن مقدار را دریافت می کند و 2 را در همان مکان x می نویسد. پردازنده 3 و پردازنده 4 دو ترتیب را با ترتیب های مختلف مشاهده می کنند. این قطعاً برای سازگاری پردازنده درست نیست ، زیرا پروتکل های انسجام حافظه پنهان مانع این امر می شود. این مقاله طولانی می شود ، بنابراین شاید وقتی به برخی از مشکلات عملکرد قفل نگاه می کنیم ، از انسجام حافظه پنهان استفاده کنیم. در حال حاضر ، این مقاله نظرسنجی مفاهیم را بسیار زیبا جمع بندی می کند. به طور فرضی برخی از شما ممکن است وقت اضافی برای خواندن گسترده داشته باشید.

    اساساً ، سازگاری پردازنده فقط مربوط به سفارش حافظه w.r.t. یک مکان حافظه واحد فرض کنید یک قفل واحد داریم ، می توان قوام پردازنده را برای آن قفل به عنوان مجموعه ای از نوشتن ها به قفل در نظر گرفت و این نوشتن ها به صورت جهانی سفارش می شوند. با این وجود خواندن مطالب مختلف ، ممکن است آنها را در زمان های مختلف مشاهده کنید.

    < مسئله ای در زمینه سازگاری انتشار است ، بیایید به شکل بالا نگاه کنیم و یک مورد نظری را در نظر بگیریم که در آن 3 پردازنده زرد ، بنفش و سبز در حال اجرای چند عملیات هستند. ابتدا زرد قفل را با استفاده از دستورالعمل مقایسه و مبادله بدست می آورد ، که 3 عملیات بدون وقفه اجرا می شود. به زودی قفل را آزاد می کند.

    عملیات نوشتن (کسب و آزاد کردن) ابتدا به رنگ بنفش منتشر می شود. با دیدن قفل آزاد شده ، بنفش از فرصت استفاده کرده و قفل را برای استفاده خود به دست آورد.

    در همین حال ، رنگ سبز فقط زردی را مشاهده می کند ، و دنباله 3 گزینه ای خود را امتحان می کند تا قفل را نیز بدست آورد ، اما سازگاری پردازنده حکم می کند که خرید op باید یک شکست باشد ، زیرا این یک عمل نوشتن است و باید در سفارش جهانی جای بگیرد. بنابراین هیچ آسیبی وارد نمی شود – سبز می تواند در آینده بدون به خطر انداختن معنای قفل مجدداً تلاش کند.

    در واقع ، پروتکل های انسجام حافظه پنهان معمولاً باعث می شود که یک پردازنده به طور انحصاری خواندن-اصلاح-نوشتن را اجرا کند در یک بار در حافظه نهان قفل. بنابراین واقعاً نمودار به شکل زیر خواهد بود:

    ممکن است فکر کنید که این ظاهر به نظر می رسد ، این نیست ما در اینجا فقط با یک مکان حافظه (قفل) سروکار داریم ، در حالی که سازگاری متوالی برای همه مکانهای حافظه ، یک ترتیب عملکرد کامل را تعیین می کند.

    در مواردی که برای رسیدن به بخش مهم نیاز به چندین قفل دارید ، این مدل صحت را نیز تضمین می کند. اثبات این امر به عنوان یک تمرین به شما خواننده اختصاص داده شده است. ?

    این خبر خوبی است! بنابراین تا زمانی که یک پردازنده به طور پیش فرض سازگاری پردازنده را به عنوان سازگاری حافظه خود پیاده سازی کند ، می توانیم با خیال راحت در بالا برنامه ایجاد کنیم تا برنامه نویسان سازگار با ترتیب را ارائه دهیم ، به شرطی که تمام دسترسی های حافظه مشترک توسط قفل محافظت شوند. در واقعیت ، سازگاری پردازنده با توجه به چگونگی پیاده سازی انسجام حافظه پنهانی ، یک مسئله کاملا منطقی است. بعداً در مورد این موضوع اطلاعات بیشتری کسب کنید.

    این ممکن است بدیهی باشد اما بیایید چند کلمه را صرف ساختن سازگاری بالای پردازنده برای اجرای سازگاری انتشار کنیم. با توجه به نیاز RCpc ، با سازگاری پردازنده بومی ، تنها چیزی که از دست رفته است ، ترتیب محلی بین کسب / انتشار و عملیات حافظه معمولی است.

    همانطور که گفتیم ، معمولاً معماری مجموعه دستورالعمل به شما امکان می دهد تا دستورالعمل های مانع یا حصار را برای اجرای نوعی سفارش محلی کنترل کنید. X86 دارای سه مورد LFENCE ، SFENCE و MFENCE است. اما در واقع فقط MFENCE از ما استفاده می کند. برای مثال ARM دارای دستورالعمل های DMB و DSB است که عملکرد مشابهی دارند. اکنون خوب هستیم که برویم!

    حتی اگر تعداد بیشماری از مدل های سازگاری وجود داشته باشد ، اما سازگاری آزاد کننده ضعیف ترین مدل موثر است. به همین دلیل است که می توانید سایر مدل های سازگاری دیگر مانند علی ، سفارش ضعیف ، PowerPC و غیره را نادیده بگیرید. جدای از این که مدل سازگاری THE x86 است ، حتی x86 TSO نیز چندان جالب نیست که بتوان آن را درک کرد.

    Coda: از اجرای spinlock ما دیدن کنید

    اکنون که نحوه کارکرد سازگاری انتشار را فهمیدید ، احتمالاً می بینید که اجرای spinlock ما در ابتدای امر می تواند به نوعی بهبود یابد زیرا نیازی نیست که دسترسی های اتمی به حالت قفل سازگاری متوالی داشته باشد. < p class = "paragraph"> سایر پیاده سازی های spinlock از ارگ های تثبیت شده وجود دارد. ساده ترین آن حماقت فیس بوک است. ساده ترین مثال ما فقط کمی متفاوت از آن است ، از این قرار:

  • حماقت سعی می کند در صورت امکان از سازگاری متوالی جلوگیری کند.
  • اگر عملیات خرید spinlock ناموفق باشد ، PAUSE x86 را اجرا می کند دستورالعمل برای کمی صبر کنید. این به پردازنده می گوید که تمام اعدامهای احتکارآمیز را بکشد تا فضای دیگر را برای یک سایت دیگر فراهم کند.

    ما قطعاً می توانیم قسمت ضعیف نظم حافظه را به راحتی در پیاده سازی خود قرار دهیم:

    class چرخش {
    عمومی: باطل قفل () { برای (؛؛) { bool used_val = false ؛ اگر (مشغول.compare_exchange_strong (انتظار_پذیر ، درست ) ، std :: memory_order_acquire ، std :: memory_order_relaxed)) بازگشت ؛ } } void باز کردن قفل () {busy.store ( false ، std :: memory_order_release)؛ } خصوصی: std :: atomic مشغول = غلط ؛

    اکنون یک چیز خنده دار وجود دارد. من آن را در اینجا پیست نمی کنم ، اما اگر باینری های این دو نسخه را تماشا کنید ، تفاوت کمی را مشاهده نمی کنید. چرا؟ حداقل فکر می کنید باید ببینید برخی از دستورالعمل های حصار حافظه در اجرای جدید حذف شده اند؟

    دلیل این است که دستورالعمل cmpxchg یک مانع ضمنی حافظه ایجاد می کند. بنابراین فراخوانی مقایسه_ارسال در x86 با ترتیب حافظه ضعیف ، چندان معنی دار نیست ، زیرا این معماری گزینه ای به کامپایلر نمی دهد.

    در مورد نسخه ، به یاد داشته باشید x86 TSO فقط با دور زدن فروشگاه ها در مکان های مختلف ، بارها را به جلو می آورد؟ هرگز زمانی نیست که دستورالعمل ها را به عقب مرتب کند ، بنابراین معناشناسی “انتشار” به طور خودکار تضمین می شود.

  • بیشتر اوقات ، متغیرهای مشترک توسط قفل محافظت می شوند. این از روزهای پردازنده وجود داشته است.
  • سازگاری انتشار از این واقعیت استفاده می کند و یک مدل بسیار عملی تر را برای سخت افزار ارائه می دهد ، در حالی که توهم سازگاری متوالی را حفظ می کند ، هنگامی که متغیرها به درستی محافظت می شوند توسط قفل ها.
  • از آنجا که می توان دوباره قفل را دوباره امتحان کرد ، سازگاری انتشار می تواند با مدل های سازگاری ضعیف تری بین گزینه های همگام سازی کار کند.
  • در x86 ، هیچ حصار حافظه ای لازم نیست برای قفل ها ، زیرا دستورالعمل های مربوطه به طور خودکار حصارهای سفارش را به ارمغان می آورند.

    این پست کاملا طولانی است. و ممنون که تا اینجا دنبال کردید. مفاهیم بنیادی معماری سرانجام از دسترس خارج شده اند ، در مقاله بعدی ما دنده را تغییر خواهیم داد و در مورد برخی موضوعات بسیار کاربردی بحث خواهیم کرد. ابتدا برخی از مفاهیم سیستم عامل و spinlock kin ، قفل mutex را معرفی خواهیم کرد. سپس ما به نحوه عملکرد آنها با حجم کاری بسیار معمول در زبانهای برنامه نویسی واقعی اشاره خواهیم کرد ، و به مسیرهای بالقوه تکنیک های پیشرفته هماهنگ سازی اشاره خواهیم کرد.