Read-write lock — umumiy resursga bir vaqtning o‘zida bir nechta o‘quvchi kirishiga, yozuvchi kirganda esa mutlaq eksklyuzivlikka ruxsat beradigan sinxronizatsiya primitivi. U ko‘p o‘qiladigan va kam o‘zgartiriladigan ma’lumotda mutexga nisbatan parallelizmni oshirishi mumkin. Yozish paytida boshqa yozuvchi ham, o‘quvchi ham bo‘limga kirmaydi.
Holatlar
Read lock olinganda faol yozuvchi bo‘lmasa o‘quvchilar soni oshadi. Write lock faqat barcha o‘quvchi chiqib, boshqa yozuvchi yo‘q bo‘lganda beriladi. Unlock hisoblagich yoki egani yangilab, kutayotgan oqimlardan birini yoxud bir guruhini uyg‘otadi. Implementatsiya ichida mutex, condition variable va navbat ishlatilishi mumkin.
Bir oqim read lockni ushlab turib write lock olishga urinsa upgrade muammosi paydo bo‘ladi. Ikki o‘quvchi bir vaqtda upgrade kutsa, ikkalasi ham bir-birining read locki bo‘shashini kutib deadlock bo‘lishi mumkin. Kutubxona maxsus upgradeable lock bermasa, read lock bo‘shatiladi va write lock qayta olinadi; orada holat o‘zgargani uchun predicate qayta tekshiriladi.
Adolatlilik
Reader-preferring siyosatda yangi o‘quvchilar faol o‘quvchilar safiga qo‘shilaveradi va yozuvchi uzoq kutishi mumkin. Writer-preferring siyosat yozuvchi navbatga kelgach yangi o‘quvchilarni to‘xtatadi, lekin o‘qish kechikishini oshiradi. Fair navbat kelish tartibini saqlashga urinadi, uning boshqaruv xarajati yuqoriroq bo‘lishi mumkin.
Starvation talabi ish yukiga bog‘liq. Konfiguratsiya keshida yozuvning bir necha millisekund kutishi mumkin, real vaqt holatida esa maksimal kutish cheklanishi kerak. Kutubxona qanday siyosat ishlatishini hujjatdan bilish zarur.
Foyda chegarasi
Kritik bo‘lim juda qisqa bo‘lsa, o‘quvchi hisoblagichi va kesh liniyasidagi atomar almashinuv mutexdan qimmatroq chiqishi mumkin. Yadro soni, o‘qish davomiyligi, yozish chastotasi va NUMA topologiyasi natijaga ta’sir qiladi. Sintetik benchmark haqiqiy contention va ma’lumot hajmini takrorlashi kerak.
O‘qish mutlaqo yon ta’sirsiz bo‘lishi lozim. Lazy cache to‘ldirish, access counter yoki iteratorning ichki holatini o‘zgartirish “o‘qish”ni amalda yozishga aylantiradi. Bunday kod read lock ostida data race keltirib chiqarishi mumkin.
Muqobil yondashuvlar
Immutable snapshot o‘quvchiga qulfsiz barqaror versiya beradi, yozuvchi esa yangi nusxa tayyorlab atomar almashtiradi. RCU o‘qish ustun bo‘lgan yadro kodida ishlatiladi, ammo eski versiyani xavfsiz ozod qilish grace period talab qiladi. Ma’lumotlar bazasidagi MVCC ham o‘quvchi va yozuvchini versiyalar orqali ajratadi.
Read-write lock tanlanganda metrikalarda read/write kutish vaqti, ushlab turish davomiyligi va starvation kuzatiladi. Oddiy mutex yetarli bo‘lsa, murakkab primitive qo‘shish to‘g‘rilik riskini oshiradi.
To‘g‘ri qo‘llanish
Qulf himoya qiladigan obyekt va barcha kirish nuqtalari dokumentatsiyada aniq ko‘rsatiladi. Bir metod write lock, boshqa metod esa xuddi shu maydonni qulfsiz o‘zgartirsa primitive foydasiz qoladi. Read lock ostida qaytarilgan pointer yoki iterator unlockdan keyin barqaror bo‘lmasligi mumkin; kerakli qiymat nusxalanadi yoki egasining umri alohida kafolatlanadi. Rekursiv chaqiriqda yana read lock olish ayrim implementatsiyada yozuvchi kutayotgan bo‘lsa bloklanishi mumkin. Shu sababli lockning reentrancy va upgrade qoidalari taxmin qilinmaydi, kutubxona hujjati bo‘yicha yoziladi va stress testda tekshiriladi.
Downgrade, ya’ni write lockdan read lockka atomar o‘tish ham hamma implementatsiyada mavjud emas. Bo‘shatib qayta olish oralig‘ida boshqa yozuvchi holatni o‘zgartirishi mumkin. Kod buni aniq qabul qiladi yoki qo‘llab-quvvatlangan primitive ishlatadi. API nomi bir xil bo‘lsa ham fairness semantikasi kutubxonalar orasida farq qiladi.
Bog‘liq tushunchalar
Mutex, Reader-writer problem, Starvation, RCU, MVCC, Critical section