En spin-lås eller spinlock ( engelsk spinlock - cyclic lock) er en lav-niveau synkronisering primitiv [1] brugt i multiprocessor systemer til at implementere gensidig udelukkelse af udførelsen af kritiske kode sektioner ved hjælp af en aktiv ventesløjfe [2] . Det bruges i tilfælde, hvor ventetiden på en lås forventes at være kort [2] eller hvis udførelseskonteksten ikke tillader overgangen til en blokeret tilstand [3] .
Spinlocks ligner mutexes , hvilket giver dig mulighed for at bruge mindre tid på at blokere en tråd, da du ikke behøver at overføre tråden til den blokerede tilstand. I tilfælde af mutexes kan det være nødvendigt at kalde skemalæggeren for at ændre trådens tilstand og tilføje den til listen over tråde, der venter på at blive låst op. Spinlocks bruger ikke skemalæggeren og bruger en venteløkke uden at ændre trådens tilstand, hvilket spilder CPU-tid på at vente på, at en anden tråd frigiver låsen. En typisk implementering af en spinlock er en simpel cyklisk kontrol af spinlock-variablen for tilgængelighed [1] .
Fysisk er en spinlock en variabel i hukommelsen og implementeres på atomoperationer, der skal være til stede i processorens instruktionssæt . Hver processor, der ønsker at få adgang til den delte ressource, skriver atomisk den betingede værdi " optaget " til denne variabel ved at bruge en analog af swap-operationen (i x86-arkitekturen - xchg). Hvis den tidligere værdi af variablen (returneret af kommandoen) var " fri ", anses den givne processor for at have adgang til ressourcen, ellers vender processoren tilbage til swap-operationen og går gennem spinlocken, indtil den frigøres. Efter at have arbejdet med en delt ressource, skal processoren - ejeren af spinlocken skrive den betingede værdi " gratis " ind i den.
Et eksempel på implementering af en spinlock i x86 assembler:
mov eax , spinlock_address mov ebx , SPINLOCK_BUSY wait_cycle: xchg [ eax ], ebx ; xchg er den eneste instruktion, der er atomisk uden præfikset lås cmp ebx , SPINLOCK_FREE jnz wait_cycle ; <kritisk sektion er fanget af denne tråd, arbejde med delt ressource er i gang her> mov eax , spinlock_address mov ebx , SPINLOCK_FREE xchg [ eax ], ebx ; brug xchg til atomændring ; sidste 3 instruktioner skal erstattes med mov [spinlock_address], SPINLOCK_FREE - ; dette vil øge hastigheden på grund af fraværet af unødvendig busblokering, og mov vil alligevel blive udført atomisk ; (men kun hvis spinlock_address er justeret på en dword-grænse)En smartere implementering ville bruge en almindelig operation i stedet for en atomoperation til polling i en løkke og en atomoperation kun til fangstforsøg. Faktum er, at implementeringen af atomare hukommelsesoperationer sker ved, at hardware blokerer systembussen af processoren i varigheden af atomoperationen (som inkluderer læsning, ændring og skrivning). Under disse tre operationer kan der ikke udføres andre operationer på bussen, hvilket reducerer ydeevnen af andre processorer i systemet (hvis de deler en fælles bus ), selvom de ikke har noget at gøre med denne spinlock.
Også brugt er de såkaldte. queued spinlocks - "køede spinlocks". I stedet for at tildele 0 eller 1 til en atomvariabel, bruger de en atomisk tilføjelse af en struktur til listens hoved, mens listens hoved er en atomvariabel af typen "pointer".
Nyttige egenskaber ved spinlocks i kø:
Spinlocks bruges til at synkronisere små sektioner af kode, når brugen af mere komplekse mekanismer er urimelig eller umulig. Implementeringen af synkroniseringsprimitiverne og trådhåndteringen kræver nødvendigvis låse for at beskytte listerne over tråde, der er klar til at udføre, og listerne over tråde, der venter på objekter. En sådan lås kan kun være en spinlock på grund af dens meget lave niveau. Således er spinlock den laveste synkroniseringsprimitiv, som implementeringen af alle de andre er baseret på.
Versioner af Windows fra inklusive Windows 7 bruger det låsefri datastrukturparadigme til at implementere afsenderen/planlæggeren. Dermed er de skånet for den eneste globale spinlock KiDispatcherLock, en af de mest belastede i OS-kernen.
Der er en udbredt opfattelse af, at i brugerapplikationer, der kører under multitasking OS, er brugen af spinlocks uacceptabel, da at vente på, at en spinlock frigives fører til en aktiv ventetid i en loop, der spilder CPU-computerressourcer, og primitiver på højt niveau skal bruges til at synkronisere brugerprogrammer, som indebærer passiv venting - hvis en given tråd ikke kan fortsætte eksekveringen, så giver den kontrol til OS og spinder ikke i en spinlock-venteløkke (som potentielt kan være uendelig). Faktisk er denne erklæring kun 100 % sand for uniprocessor-systemer. I mange tilfælde fører brug af spinlocks i SMP - konfigurationer til effektivitetsgevinster, hvis polling og erhvervelse af en spinlock er hurtigere end at kalde en mutex-acquisition i kernen.
Hovedkriteriet her er påstand - "rigiditeten" af konkurrencen om ressourcen. En let indlæst ressource, der ikke er et populært eksekveringssted, opfører sig anderledes end en tungt indlæst ressource, der fanges og deallokeres meget ofte.
Derudover er der i de samme Windows varianter af mutexes (f.eks. den velkendte CRITICAL_SECTION/EnterCriticalSection/LeaveCriticalSection, eller dets synonym i OS-kernen - FAST_MUTEX/ExAcquireFastMutex/ExReleaseFastMutex), som først fungerer som en spinlock, vha. en værdiafstemning i hukommelsen, og først derefter, efter et stort antal afstemninger, gå til kernen for at blokere vente. Sådanne objekter kombinerer de bedste kvaliteter af spinlocks (minimumsomkostninger ved optagelse) og mutexes (intet spild af CPU-ressourcer til polling).
Tilfælde, hvor brugen af spinlocks i brugerrummet giver en håndgribelig effekt:
Men brugen af "hurtige mutexes" såsom Win32's CRITICAL_SECTION gør alt ovenstående unødvendigt i brugerrummet.
Tilfælde, hvor brugen af spinlocks ikke er berettiget og er spild af processorressourcer:
På moderne processorer kan dvalecyklussen være meget hurtig på grund af de særlige kendetegn ved pipeline-arkitekturen, som ud over at vikle tomgangscyklusser kan føre til mere intens opvarmning end under normal drift.
Pentium 4 og senere modeller af Intel - processorer introducerede en speciel assembler-instruktion om at indsætte i en pause-loop ( opcode 0xf3 0x90, svarende til rep nop for kompatibilitet med ældre processorer), som har til formål at instruere processoren om, at denne cyklus er en vente-løkke, og giver processoren mulighed for at understøtte flere tråde på den samme kerne, gå videre til næste tråd.
Versioner af Windows siden Windows 7 er optimeret til at køre som "gæst" i en virtuel maskine, og i stedet for at holde pause i tilfælde, hvor operativsystemet kører som gæst, vil et særligt opkald "underrette hypervisoren om, at vi er i en venteløkke" anvendes.
Spinlock med automatisk vækst, indtil en fuldgyldig mutex er fanget efter et vist antal cyklusomdrejninger er udløbet, bruges for eksempel i kritiske dele af Windows til optimering, som består i fravær af kald til mutex'en i mangel af konkurrence for en ressource.