A spinlock vagy spinlock ( angolul spinlock - ciklikus zár) egy alacsony szintű szinkronizációs primitív [1] , amelyet többprocesszoros rendszerekben használnak a kritikus kódszakaszok végrehajtásának kölcsönös kizárására egy aktív várakozási hurok segítségével [2] . Olyan esetekben használatos, amikor a zárolásra való várakozás várhatóan rövid lesz [2] , vagy ha a végrehajtási környezet nem teszi lehetővé a blokkolt állapotba való átmenetet [3] .
A spinlockok hasonlóak a mutexekhez , így kevesebb időt tölthet egy szál blokkolásával, mivel nem kell a szálat blokkolt állapotba vinnie. Mutexek esetén előfordulhat, hogy meg kell hívni az ütemezőt, hogy módosítsa a szál állapotát, és hozzáadja a feloldásra váró szálak listájához. A spinlockok nem használják az ütemezőt, és várakozási ciklust használnak a szál állapotának megváltoztatása nélkül, ami a CPU-időt vesztegeti arra várva, hogy egy másik szál feloldja a zárolást. A spinlock tipikus megvalósítása a spinlock változó elérhetőségének egyszerű ciklikus ellenőrzése [1] .
Fizikailag a spinlock egy változó a memóriában, és olyan atomi műveleteken valósul meg , amelyeknek jelen kell lenniük a processzor utasításkészletében . Minden processzor, amely hozzá akar férni a megosztott erőforráshoz , a swap művelet egy analógjával (x86 architektúrában - xchg) a " busy " feltételes értéket írja ebbe a változóba. Ha a változó előző (a parancs által visszaadott) értéke „ free ” volt, akkor az adott processzor hozzáfért az erőforráshoz, ellenkező esetben a processzor visszatér a swap művelethez, és a spinlockon keresztül hurkol, amíg fel nem szabadul. A megosztott erőforrással végzett munka után a processzornak - a spinlock tulajdonosának - be kell írnia a " szabad " feltételes értéket.
Példa a spinlock megvalósítására x86 assemblerben:
mov eax , spinlock_address mov ebx , SPINLOCK_BUSY várakozás_ciklus: xchg [ eax ], ebx ; Az xchg az egyetlen utasítás, amely atomikus a lock cmp ebx előtag nélkül , SPINLOCK_FREE jnz wait_cycle ; <a kritikus szakaszt ez a szál rögzíti, itt folyamatban van a megosztott erőforrással végzett munka> mov eax , spinlock_address mov ebx , SPINLOCK_FREE xchg [ eax ], ebx ; használja az xchg-t az atomi változáshoz ; az utolsó 3 utasítást le kell cserélni a mov [spinlock_address]-re, SPINLOCK_FREE - ; ez növeli a sebességet a szükségtelen buszlezárás hiánya miatt, és a mov egyébként atomosan fog végrehajtódni ; (de csak akkor, ha a spinlock_address egy dword határon van igazítva)Egy intelligensebb megvalósítás normál műveletet használna az atomos művelet helyett a hurokban történő lekérdezéshez, és egy atomi műveletet csak a rögzítési kísérletekhez. Az a tény, hogy az atomi memóriaműveletek végrehajtása úgy történik, hogy a hardver blokkolja a rendszerbuszt a processzor által az atomi művelet (amely magában foglalja az olvasást, a módosítást és az írást) idejére. E három művelet alatt a buszon nem hajtható végre más művelet, ami csökkenti a rendszerben lévő többi processzor teljesítményét (ha közös buszon osztoznak ), még akkor sem, ha semmi közük ehhez a spinlockhoz.
Szintén használatosak az ún. sorban álló spinlockok - "sorba állított spinlockok". Ahelyett, hogy egy atomi változóhoz 0-t vagy 1-et rendelnének, egy szerkezet atomi hozzáadását használják a lista fejéhez, míg a lista feje egy "mutató" típusú atomi változó.
A sorban álló spinlockok hasznos tulajdonságai:
A spinlockokat a kód kis szakaszainak szinkronizálására használják, ha bonyolultabb mechanizmusok használata ésszerűtlen vagy lehetetlen. A szinkronizálási primitívek és a szálkezelő megvalósítása szükségszerűen zárolást igényel a végrehajtásra kész szálak és az objektumokon várakozó szálak listájának védelmére. Egy ilyen zár nagyon alacsony szintje miatt csak spinlock lehet. Így a spinlock a legalacsonyabb szinkronizációs primitív, amelyen az összes többi megvalósítása alapul.
A Windows 7-et is magában foglaló Windows-verziói a zárolásmentes adatszerkezetek paradigmáját használják a diszpécser/ütemező megvalósításához. Így megkímélték őket az egyetlen globális spinlock KiDispatcherLocktól, amely az egyik legerősebben terhelt az operációs rendszer kernelében.
Széles körben elterjedt az a vélemény, hogy a többfeladatos operációs rendszer alatt futó felhasználói alkalmazásokban a spinlockok használata elfogadhatatlan, mivel a spinlock felszabadítására való várakozás aktív várakozáshoz vezet egy ciklusban, ami pazarolja a CPU számítási erőforrásait, és magas szintű primitíveket kell használni. felhasználói programok szinkronizálására szolgál, ami passzív várakozást jelent - ha egy adott szál nem tudja folytatni a végrehajtást, akkor az OS-nek adja az irányítást, és nem pörög spinlock várakozási hurokban (amely potenciálisan végtelen is lehet). Valójában ez az állítás 100%-ban csak egyprocesszoros rendszerekre igaz. Sok esetben a spinlockok használata az SMP konfigurációkban hatékonyságnövekedést eredményez, ha a lekérdezés és a spinlock beszerzése gyorsabb, mint a kernelben lévő mutex adatgyűjtés meghívása.
A fő kritérium itt a vita – az erőforrásokért folyó verseny "merevsége". Egy enyhén betöltött erőforrás, amely nem népszerű végrehajtási webhely, másképp viselkedik, mint egy erősen terhelt erőforrás, amelyet nagyon gyakran rögzítenek és felszabadítanak.
Ezenkívül ugyanabban a Windows-ban vannak különféle mutexek (például a jól ismert CRITICAL_SECTION/EnterCriticalSection/LeaveCriticalSection vagy szinonimája az operációs rendszer kernelében - FAST_MUTEX/ExAcquireFastMutex/ExReleaseFastMutex), amelyek először spinként működnek. értéklekérdezés a memóriában, és csak ezután, sok lekérdezés után lépjen a kernelhez a blokkoló várakozásra. Az ilyen objektumok a spinlockok (minimális rögzítési költség) és a mutexek (nem pazarolják a CPU-erőforrást lekérdezéshez) legjobb tulajdonságait egyesítik.
Azok az esetek, amikor a spinlockok használata a felhasználói térben kézzelfogható hatást fejt ki:
Azonban a "gyors mutexek", mint például a Win32 CRITICAL_SECTION használata, a fentiek mindegyikét szükségtelenné teszi a felhasználói térben.
Olyan esetek, amikor a spinlockok használata nem indokolt és a processzor erőforrásainak pazarlása:
A modern processzorokon a csővezetékes architektúra sajátosságai miatt nagyon gyors lehet az alvási ciklus, ami a tekercses üresjárati ciklusok mellett a normál működésnél intenzívebb felfűtéshez vezethet.
A Pentium 4 és az Intel processzorok újabb modelljei bevezettek egy speciális assembler utasítást a szünet hurokba való beillesztésére ( opcode 0xf3 0x90, hasonló a rep nop-hoz a régebbi processzorokkal való kompatibilitás érdekében), amely arra utasítja a processzort, hogy ez a ciklus egy várakozási ciklus, és lehetővé teszi a processzor számára, hogy több szálat támogasson ugyanazon a magon, lépjen tovább a következő szálra.
A Windows 7 óta tartó Windows verziói úgy vannak optimalizálva, hogy „vendégként” fussanak egy virtuális gépen, és a szünet helyett olyan esetekben, amikor az operációs rendszer vendégként fut, egy speciális hívás „értesíti a hipervizort, hogy várakozási ciklusban vagyunk”. használt.
A spinlock automatikus növekedéssel, amíg egy teljes értékű mutex rögzítésre nem kerül bizonyos számú ciklusfordulat lejárta után, például a Windows kritikus szakaszaiban az optimalizálás érdekében, ami abból áll, hogy verseny hiányában nem hívják a mutexet. egy erőforrásért.