عنوان:

‫نوشتن یک جمع‌آورکننده زباله .NET در C# – قسمت 7: علامت‌گذاری هندل‌ها


نویسنده: راویAI
تاریخ: ۱۴۰۴/۱۱/۲۳ ۰۱:۳۱
آدرس: www.dntips.ir
در قسمت قبلی این مجموعه، پیاده‌سازی فاز علامت‌گذاری و فاز جاروب جمع‌آورکننده زباله آغاز شد. در آنجا توضیح داده شد که ریشه‌ها به سه دسته تقسیم می‌شوند: متغیرهای محلی و فضای ذخیره‌سازی thread-static، هندل‌های GC و صف نهایی‌سازی. تا کنون، تنها دسته اول اسکن شده بود. اکنون به اسکن هندل‌های GC می‌پردازیم. برای یادآوری، در قسمت دوم از یک آرایه با اندازه ثابت برای ذخیره هندل‌ها استفاده شد و اشاره شد که این موضوع باید در آینده مورد بازبینی قرار گیرد. برای علامت‌گذاری، نیاز به بازیابی هندل‌ها بر اساس نوع داریم، بنابراین این زمان مناسبی برای پیاده‌سازی یک فضای ذخیره‌سازی مناسب است. فضای ذخیره‌سازی هندل‌ها باید الزامات زیر را برآورده کند: فضای ذخیره‌سازی باید بتواند هندل‌ها را بر اساس نوع به طور کارآمد بازیابی کند. پس از تخصیص، آدرس یک هندل نباید تغییر کند. می‌توان از آرایه‌های پین‌شده استفاده کرد، اما ترجیح داده می‌شود که این موضوع با استفاده از حافظه native اعمال شود. باید بتوانیم تعداد نامحدودی هندل را ذخیره کنیم. تخصیص هندل‌های جدید باید thread-safe باشد. الزامات اول را می‌توان با ایجاد یک فضای ذخیره‌سازی مجزا برای هر نوع هندل به راحتی برآورده کرد. برای دو الزام بعدی، چاره‌ای نداریم: از آنجایی که نمی‌توانیم فضای ذخیره‌سازی خود را دوباره تخصیص دهیم (زیرا این امر باعث می‌شود هندل‌ها در حافظه جابجا شوند)، از یک رویکرد مبتنی بر segment استفاده خواهیم کرد، که در آن segmentهای جدید را در صورت نیاز برای پذیرش تخصیص‌های جدید اختصاص می‌دهیم. کلاس GCHandleStore به طور مداوم سعی می‌کند سر لیست آزاد را با استفاده از عملیات CAS (مقایسه و جایگزینی) اتمی جایگزین کند، تا زمانی که یا موفق شود یا _freeHead به -1 برسد (که به این معنی است که کسی آخرین slot آزاد را تخصیص داده است). با این حال، یک race condition ظریف در این کد وجود دارد، اگر سناریوی زیر را در نظر بگیریم: thread T1 مقدار _freeHead = 5 را می‌خواند و slot5->ExtraInfo = 6 را می‌خواند. thread T2 مقدار 5 را pop می‌کند (head اکنون 6 است)، مقدار 6 را pop می‌کند (head اکنون 7 است)، سپس مقدار 5 را push می‌کند (head دوباره 5 است، اما با slot5->ExtraInfo = 7). عملیات CAS thread T1 موفق می‌شود (head دوباره 5 است) و _freeHead را به 6 تنظیم می‌کند (منسوخ شده)، node 7 را از دست می‌دهد و زنجیره را خراب می‌کند. این سناریو به عنوان مشکل ABA شناخته می‌شود. راه حل رایج، بسته‌بندی یک tag است...


مشاهده مطلب اصلی