A végrehajtás szála

Végrehajtási szál (thread; angolból  thread  - thread) - a feldolgozás legkisebb egysége, amelynek végrehajtását az operációs rendszer kernelje rendelheti hozzá . A szálak és folyamatok megvalósítása operációs rendszerenként eltérő, de a legtöbb esetben a végrehajtási szál egy folyamaton belül található. Egyazon folyamaton belül több végrehajtási szál is létezhet, amelyek megoszthatják az erőforrásokat, például a memóriát , míg a folyamatok nem osztják meg ezeket az erőforrásokat. Különösen a végrehajtási szálak osztoznak egy folyamat utasítássorozatán (kódjában) és kontextusán , a változók értékein (processzorregiszterek és hívásverem ), amelyekkel bármikor rendelkeznek.

Hasonlatként egy folyamat szálai több szakács együttdolgozásához hasonlíthatók. Mindannyian ugyanazt az ételt főzik, ugyanazt a szakácskönyvet olvassák ugyanazzal a recepttel, és követik annak utasításait, és nem feltétlenül mindegyik ugyanazon az oldalon olvassa.

Egyetlen processzoron a többszálú feldolgozás általában időmultiplexeléssel történik (mint a multitasking esetében ): a processzor különböző végrehajtási szálak között vált. Ez a kontextusváltás általában elég gyakran fordul elő ahhoz, hogy a felhasználó egyidejűnek érzékelje a szálak vagy feladatok végrehajtását. A többprocesszoros és többmagos rendszerekben a szálak vagy feladatok ténylegesen futhatnak párhuzamosan is, és minden processzor vagy mag külön szálat vagy feladatot dolgoz fel.

Sok modern operációs rendszer támogatja az időszeletelést a folyamatütemezőből és a többprocesszoros végrehajtási szálakat egyaránt. Az operációs rendszer kernel lehetővé teszi a programozók számára a végrehajtási szálak rendszerhívási felületen keresztül történő vezérlését . Egyes kernel-megvalósítások kernelszálként , míg mások könnyű folyamatként ( LWP ) utalnak rá, amely egy  speciális kernelszál, amely ugyanazon az állapoton és adatokon osztozik.

A programok felhasználói térbeli végrehajtási szálakkal rendelkezhetnek, amikor a szálakat időzítőkkel, jelekkel vagy más módszerekkel hozzák létre a végrehajtás megszakításához és időszeletelés létrehozásához egy adott helyzethez ( Ad hoc ).


Különbség a folyamatoktól

A végrehajtási szálak abban különböznek a hagyományos többfeladatos operációs rendszer folyamatoktól , hogy:

Az olyan rendszerekről, mint a Windows NT és az OS/2 , állítólag "olcsó" szálak és "drága" folyamatok vannak. Más operációs rendszereken a végrehajtási szálak és a folyamatok közötti különbség nem olyan nagy, kivéve a címtérváltás költségeit, amely asszociatív fordítási puffer használatát foglalja magában .

Többszálú

A többszálú, mint széles körben elterjedt programozási és kódvégrehajtási modell lehetővé teszi több szál futtatását egyetlen folyamaton belül. Ezek a végrehajtási szálak megosztják a folyamat erőforrásait, de önállóan is futhatnak. A többszálú programozási modell kényelmes absztrakciót biztosít a fejlesztőknek a párhuzamos végrehajtásból. A technológia talán legérdekesebb alkalmazása azonban az, amikor egyetlen folyamatra alkalmazzák, ami lehetővé teszi párhuzamos végrehajtását többprocesszoros rendszeren .

A többszálú program ezen előnye lehetővé teszi, hogy gyorsabban fusson több processzorral rendelkező számítógépes rendszereken , többmagos processzorokon vagy gépcsoportokon , mivel a programszálak természetesen alkalmasak a folyamatok valóban párhuzamos végrehajtására. Ebben az esetben a programozónak nagyon óvatosnak kell lennie, hogy elkerülje a versenykörülményeket és más, nem intuitív viselkedést. Az adatok megfelelő manipulálása érdekében a végrehajtási szálaknak gyakran át kell menniük egy találkozási eljáráson, hogy az adatokat a megfelelő sorrendben dolgozzák fel. A végrehajtási szálakhoz mutexekra is szükség lehet (amelyeket gyakran szemaforokkal valósítanak meg ), hogy megakadályozzák a megosztott adatok egyidejű módosítását vagy olvasását a módosítási folyamat során. Az ilyen primitívek hanyag használata holtponthoz vezethet .

A többszálú feldolgozás egy másik felhasználási módja, még egyprocesszoros rendszerek esetében is, hogy az alkalmazás képes válaszolni a bemenetre. Az egyszálú programokban, ha a végrehajtás fő szálát egy régóta futó feladat végrehajtása blokkolja, előfordulhat, hogy az egész alkalmazás lefagyott állapotban van. Ha az ilyen régóta futó feladatokat áthelyezi egy munkaszálra , amely párhuzamosan fut a fő szállal, lehetővé válik, hogy az alkalmazások továbbra is válaszoljanak a felhasználói bevitelre, miközben a feladatok a háttérben futnak. Másrészt a legtöbb esetben a többszálú feldolgozás nem az egyetlen módja annak, hogy egy program érzékeny legyen. Ugyanez érhető el aszinkron I/O-val vagy jelekkel UNIX-ban. [egy]

Az operációs rendszerek kétféle módon ütemezhetik a szálakat:

  1. A megelőző többszálú feldolgozást általában kiváló megközelítésnek tekintik, mivel lehetővé teszi az operációs rendszer számára, hogy meghatározza, mikor kell kontextusváltásra sor kerülni. A prioritásos multithreading hátránya, hogy a rendszer rosszkor tud kontextusváltást végrehajtani, ami prioritás-inverzióhoz és egyéb negatív hatásokhoz vezethet, amelyeket kooperatív többszálú kezeléssel elkerülhetünk.
  2. A kooperatív többszálú feldolgozás magukra a szálakra támaszkodik, és feladja az irányítást, ha a végrehajtási szálak töréspontokon vannak. Ez problémákat okozhat, ha a végrehajtási szál addig vár egy erőforrásra, amíg az elérhetővé válik.

Az 1990-es évek végéig az asztali processzorok nem rendelkeztek többszálú támogatással, mivel a szálak közötti váltás általában lassabb volt, mint a teljes folyamatkörnyezetváltás . A beágyazott processzorok , amelyek magasabb követelményeket támasztanak a valós idejű viselkedéssel szemben , támogathatják a többszálú feldolgozást azáltal, hogy csökkentik a szálak közötti váltás idejét, esetleg dedikált regiszterfájlokat rendelnek minden egyes végrehajtási szálhoz, ahelyett, hogy elmentenének/visszaállítanának egy közös regiszterfájlt. Az 1990-es évek végén az Intel Pentium 4 processzorral ellátott asztali számítógépekbe is eljutott az a gondolat, hogy egyszerre több szálból hajtsanak végre utasításokat, az úgynevezett szimultán többszálú kezelést, az úgynevezett Hyper-Threadinget . Ezután kizárták az Intel Core és Core 2 architektúra processzoraiból , de később visszaállították a Core i7 architektúrában .

A többszálú kezelés kritikusai azzal érvelnek, hogy a szálak használatának növelése jelentős hátrányokkal jár:

Bár a végrehajtási szálak kis lépésnek tűnnek a szekvenciális számítástechnikától, valójában hatalmas ugrást jelentenek. Feladják a szekvenciális számítástechnika legfontosabb és legvonzóbb tulajdonságait: az érthetőséget, a kiszámíthatóságot és a determinizmust. A végrehajtási szálak, mint a számítási modell, figyelemreméltóan nem determinisztikusak, és ennek a nem-determinizmusnak a csökkentése a programozó feladata lesz. [2]

Folyamatok, kernelszálak, felhasználói szálak és szálak

A folyamat a "legnehezebb" kernel ütemezési egysége. A folyamathoz szükséges saját erőforrásokat az operációs rendszer osztja ki. Az erőforrások közé tartozik a memória, a fájlkezelők, a foglalatok, az eszközfogantyúk és az ablakok. A folyamatok csak olyan explicit módszereken keresztül használnak időmegosztási címteret és erőforrásfájlokat, mint például a fájlleírók és a megosztott memóriaszegmensek öröklése. A folyamatokat általában előre konvertálják többfeladatos végrehajtási módba.

A kernelszálak a kernel ütemezésének "könnyű" egységei közé tartoznak. Minden folyamaton belül van legalább egy végrehajtási kernelszál. Ha egy folyamaton belül több kernel-végrehajtási szál is létezhet, akkor ugyanazon a memória- és erőforrásfájlon osztoznak. Ha az operációs rendszer ütemező végrehajtási folyamata előtérben van, akkor a kernelszálak is előtérben vannak a többfeladatos munkavégzéssel. A kernelszálaknak nincs saját erőforrásuk, kivéve a hívásveremet , a processzor regisztereinek másolatát , beleértve a programszámlálót és a szál helyi memóriáját (ha van). A kernel kijelölhet egy végrehajtási szálat a rendszer minden egyes logikai magjához (mert minden processzor több logikai magra osztja magát, ha támogatja a többszálú feldolgozást, vagy fizikai magonként csak egy logikai magot támogat, ha nem támogatja a többszálú feldolgozást), vagy cserélje ki a blokkolt végrehajtási szálakat. A kernelszálak azonban sokkal több időt vesznek igénybe, mint a felhasználói szálak cseréje.

A végrehajtási szálakat néha a könyvtárak felhasználói terében valósítják meg , ebben az esetben ezeket felhasználói végrehajtási szálaknak nevezik . A kernel nem tud róluk, ezért a felhasználói térben kezelik és ütemezik őket. Egyes megvalósításokban a felhasználói végrehajtási szálak a végrehajtás néhány legfelső kernelszálán alapulnak , hogy kihasználják a többprocesszoros gépek előnyeit (M:N modellek). Ebben a cikkben a "szál" kifejezés alapértelmezés szerint (a "kernel" vagy az "egyéni" minősítő nélkül) a "kernelszálra" utal. A virtuális gépekkel megvalósított felhasználói végrehajtási szálakat "végrehajtási zöld szálaknak" is nevezik. Az egyéni végrehajtási szálak általában gyorsan létrehozhatók és könnyen kezelhetők, de nem tudják kihasználni a többszálú és többfeldolgozási szálakat. Blokkolni tudják, ha az összes hozzá tartozó kernelszál foglalt, még akkor is, ha néhány felhasználói szál futásra készen áll.

A szálak még "könnyebb" ütemezési egységek a kooperatív többfeladatos munkavégzéshez kapcsolódóan : egy futó szálnak kifejezetten "át kell adnia" a végrehajtási jogot más szálaknak, ami sokkal könnyebbé teszi a megvalósításukat, mint a kernelszálak vagy felhasználói szálak megvalósítása. A Fibers ütemezhető, hogy ugyanazon a folyamaton belül bármely végrehajtási szálon fussanak. Ez lehetővé teszi az alkalmazások számára, hogy teljesítménynövekedést érjenek el saját ütemezésük kezelésével, ahelyett, hogy a kernelütemezőre hagyatkoznának (amely esetleg nincs erre konfigurálva). A párhuzamos programozási környezetek, mint például az OpenMP , általában szálakon keresztül valósítják meg feladataikat.

Problémák a szálakkal és szálakkal

Párhuzamosság és adatszerkezetek

Ugyanabban a folyamatban a végrehajtási szálak ugyanazt a címteret használják. Ez lehetővé teszi az egyidejűleg végrehajtott kódok szoros összekapcsolását és kényelmes adatcserét a folyamatok közötti kommunikáció többletterhelése és bonyolultsága nélkül . Amikor több szál osztozik még egyszerű adatstruktúrákon is, fennáll a versenyhelyzet veszélye, ha egynél több processzor utasításra van szükség az adatok frissítéséhez: előfordulhat, hogy két végrehajtási szál egyszerre próbálja meg frissíteni az adatstruktúrákat, és a végén adatok, amelyek állapota eltér a várttól. A versenykörülmények által okozott hibákat nagyon nehéz reprodukálni és elkülöníteni.

Ennek elkerülése érdekében a végrehajtási alkalmazásprogramozási felületek (API-k) szálai szinkronizálási primitíveket , például mutexet kínálnak , hogy megakadályozzák az adatstruktúrák egyidejű elérését. Egyprocesszoros rendszereken a zárolt mutexhez hozzáférő szálnak le kell állnia, és ezért kontextusváltást kell kezdeményeznie. Többprocesszoros rendszereken egy végrehajtási szál spinlockot kaphat a mutex lekérdezése helyett . Mindkét módszer ronthatja a teljesítményt, és arra kényszerítheti az SMP rendszerek processzorát, hogy versenyezzenek a memóriabuszért, különösen, ha a zárolási modularitás szintje túl magas.

I/O és ütemezés

Az egyedi szálak és szálak megvalósítása általában teljes mértékben a felhasználói térben történik. Ennek eredményeként a felhasználói szálak és szálak közötti kontextusváltás ugyanabban a folyamatban nagyon hatékony, mivel egyáltalán nem igényel interakciót a kernellel. A kontextusváltást helyileg úgy hajtják végre, hogy elmentik a futó felhasználói szál vagy szál által használt processzorregisztereket, majd betöltik az új végrehajtáshoz szükséges regisztereket. Mivel az ütemezés a felhasználói térben történik, az ütemezési házirend könnyen szabható egy adott program követelményeihez.

A felhasználói szálak (szemben a kernelszálakkal) és szálak rendszerhívási zárainak használatának azonban megvannak a maga problémái. Ha egy felhasználói szál vagy szál rendszerhívást hajt végre, a folyamatban lévő többi szál és szál nem futhat, amíg a feldolgozás be nem fejeződik. Egy ilyen probléma tipikus példája az I / O műveletek teljesítményével kapcsolatos. A legtöbb program az I/O szinkron végrehajtására készült. Amikor egy I/O-t kezdeményez, rendszerhívás történik, és az nem tér vissza, amíg be nem fejeződik. Időközben az egész folyamatot blokkolja a kernel, és nem futhat, ami működésképtelenné teszi a folyamat többi felhasználói szálát és szálát.

A probléma általános megoldása az, hogy külön I/O API-t biztosítanak, amely belső, nem blokkoló I/O-t használó szinkron interfészt valósít meg, és egy másik felhasználói szálat vagy szálat indítanak el az I/O feldolgozása közben. Hasonló megoldások biztosíthatók a rendszerhívások blokkolására. Ezenkívül a program úgy írható, hogy elkerülje a szinkron I/O vagy más blokkoló rendszerhívásokat.

A SunOS 4.x bevezette az úgynevezett " könnyű folyamatokat " vagy LWP -ket . A NetBSD 2.x + és a DragonFly BSD az LWP-t kernelszálként valósította meg (1:1 modell). A SunOS 5.2 és a SunOS 5.8, valamint a NetBSD 2 és a NetBSD 4 kétszintű modellt valósított meg, kernelszálonként egy vagy több felhasználói szálat használva (M:N modell). A SunOS 5.9 és újabb, valamint a NetBSD 5 megszüntette a felhasználói szálak támogatását, és visszatért az 1:1-es modellhez. [3] A FreeBSD 5 az M:N modellt valósította meg. A FreeBSD 6 támogatja az 1:1 és az M:N modelleket, és a felhasználó kiválaszthatja, hogy melyiket használja az adott programban az /etc/libmap.conf segítségével. A FreeBSD 7-es verziójában az 1:1-es modell lett az alapértelmezett, a FreeBSD 8-as és újabb verzióiban pedig az M:N modell egyáltalán nem támogatott.

A kernelszálak használata leegyszerűsíti a felhasználói kódot azáltal, hogy a többszálú kezelés bonyolultabb aspektusait áthelyezi a kernelbe. A programnak nem kell ütemeznie a végrehajtási szálakat és az explicit processzorrögzítéseket. A felhasználói kód az ismert eljárási stílusban írható, beleértve az API-hívások blokkolását, anélkül, hogy megtagadnák más szálak hozzáférését a processzorhoz. A kernelszálak azonban bármikor kontextusváltást okozhatnak a végrehajtási szálak között, és ezáltal felfedhetnek olyan verseny- és párhuzamossági hibákat, amelyek esetleg nem fordulnak elő. SMP rendszereken ez még súlyosabb, mivel a kernelszálak szó szerint párhuzamosan futhatnak különböző processzorokon.

Modellek

1:1 (kernel szintű szálak)

A felhasználó által az 1-1 modellben létrehozott végrehajtási szálak elküldhető kernel entitásoknak felelnek meg. Ez a szálfűzés lehető legegyszerűbb megvalósítása. A Windows API a kezdetektől ezt a megközelítést alkalmazta. Linuxon a szokásos C-könyvtár valósítja meg ezt a megközelítést ( a POSIX Thread Library -n keresztül, a régebbi verziókban pedig a LinuxThreads -en keresztül ). Ugyanezt a megközelítést használja a Solaris OS , a NetBSD és a FreeBSD .

N:1 (felhasználói szintű szálak)

Az N:1 modell feltételezi, hogy az összes felhasználói szintű végrehajtási szál egyetlen kernelszintű ütemezési entitáshoz van leképezve, és a kernel semmit sem tud az alkalmazásszálak összetételéről. Ezzel a megközelítéssel a kontextusváltás nagyon gyorsan elvégezhető, ráadásul még egyszerű, többszálas rendszermagokon is megvalósítható. Egyik fő hátránya azonban, hogy nem tudja kihasználni a hardveres gyorsítás előnyeit többszálú processzorokon vagy többprocesszoros számítógépeken, mert egy adott időpontban csak egy szál végrehajtása ütemezhető. Ezt a modellt a GNU Portable Threads használják.

M:N (vegyes streaming)

Az M:N modellben néhány M számú alkalmazási szál van leképezve néhány N számú kernel entitásra vagy "virtuális processzorra". A modell kompromisszum a kernel szintű ("1:1") és a felhasználói szintű ("N:1") modell között. Általánosságban elmondható, hogy az "M:N" rendszerszál-fűzés megvalósítása bonyolultabb, mint a kernel vagy a felhasználói szálak végrehajtása, mivel sem a kernel, sem a felhasználói terület nem igényel kódmódosítást. Egy M:N megvalósításban a szálkönyvtár felelős a felhasználói szálak végrehajtásának ütemezéséért az elérhető ütemezési entitásokon. Ugyanakkor a szálkontextus váltás nagyon gyorsan megtörténik, mivel a modell elkerüli a rendszerhívásokat. Azonban nő a bonyolultság és a prioritási inverziók valószínűsége, valamint a nem optimális ütemezés a felhasználói ütemező és a kernelütemező közötti kiterjedt (és költséges) koordináció nélkül.

Megvalósítások

A szálaknak sok különböző, inkompatibilis megvalósítása létezik. Ezek magukban foglalják a kernel szintű megvalósításokat és a felhasználói szintű megvalósításokat is. Leggyakrabban többé-kevésbé szorosan követik a POSIX Threads interfész szabványt .

Példák a szál megvalósítására a kernel szintjén

Példák felhasználói szintű szál-megvalósításokra

  • GNU hordozható szálak
  • FSU Pthreads
  • Apple Thread Manager
  • REALbasic (beleértve egy API-t a szál megosztásához)
  • Netscape Portable Runtime (beleértve a szálak felhasználói térbeli megvalósítását)

Példák vegyes adatfolyam-megvalósításokra

  • Az "ütemező aktiválások" a NetBSD natív POSIX szálfűző alkalmazáskönyvtárában használatosak (M:N modell, szemben az 1:1-es kernel vagy felhasználói tér alkalmazásmodelljével)
  • Marcel a PM2 projektből
  • OS a Tera/Cray MTA szuperszámítógéphez
  • Windows 7

Példák szálas megvalósításokra

A Fibers az operációs rendszer támogatása nélkül is megvalósítható, bár egyes operációs rendszerek és könyvtárak kifejezetten támogatják őket.

  • A Win32 könyvtár API-t tartalmaz a fibers számára [4] (Windows NT 3.51 SP3 és újabb)
  • Ruby mint a " zöld szálak " megvalósítása

Programozási nyelv támogatása

Sok programozási nyelv eltérően támogatja a szálakat. A legtöbb C és C++ implementáció (a C++11 szabvány előtt) nem nyújt közvetlen támogatást maguknak a szálaknak, de hozzáférést biztosít az operációs rendszer által biztosított szálakhoz API -n keresztül . Egyes magasabb szintű (általában többplatformos) programozási nyelvek, mint például a Java , a Python és a .NET , a szálak fűzését absztrakt, platformspecifikus módon biztosítják a fejlesztő számára, amely különbözik a szálak futásidejű megvalósításától. Számos más programozási nyelv is megpróbálja teljesen elvonatkoztatni a párhuzamosság és a szálfűzés fogalmát a fejlesztőtől ( Cilk , OpenMP , MPI ...). Egyes nyelveket kifejezetten a párhuzamosságra terveztek (Ateji PX, CUDA ).

Egyes értelmező programozási nyelvek, mint például a Ruby és a CPython (a Python egyik implementációja), támogatják a szálakat, de van egy korlátozásuk Global Interpreter Lock (GIL) néven. A GIL egy értelmező által végrehajtott kivételmutex, amely megakadályozhatja, hogy az interpreter egyszerre két vagy több szálban értelmezze az alkalmazáskódot, hatékonyan korlátozva a párhuzamosságot a többmagos rendszereken (főleg a processzorhoz kötött szálaknál, nem a hálózathoz kötött szálakban). ).

Lásd még

Jegyzetek

  1. Szergej Ignacsenko. Egyszálú: Vissza a jövőbe? Archivált : 2011. szeptember 29. a Wayback Machine Overload #97 -ben
  2. Edward A. Lee. A probléma a szálakkal . számú műszaki jelentés UCB/EECS-2006-1 . EECS Tanszék, University of California, Berkeley (2006. január 10.). Letöltve: 2012. május 30. Az eredetiből archiválva : 2012. június 26.
  3. Oracle and Sun (downlink) . Letöltve: 2011. november 30. Az eredetiből archiválva : 2009. március 27.. 
  4. CreateFiber archiválva : 2011. július 17. a Wayback Machine MSDN -nél

Irodalom

  • David R. Butenhof. Programozás POSIX szálakkal. Addison Wesley. ISBN 0-201-63392-2
  • Bradford Nichols, Dick Buttlar, Jacqueline Proulx Farell. Pthread programozás. O'Reilly & Associates. ISBN 1-56592-115-1
  • Charles J. Northrup. Programozás UNIX szálakkal. John Wiley & Sons. ISBN 0-471-13751-0
  • Mark Walmsley. Többszálú programozás C++ nyelven. Springer. ISBN 1-85233-146-1
  • Paul Hyde. Java szál programozás. Sams. ISBN 0-672-31585-8
  • Bill Lewis. Threads Primer: Útmutató a többszálú programozáshoz. Prentice Hall. ISBN 0-13-443698-9
  • Steve Kleiman, Devang Shah, Bart Smaalders. Programozás szálakkal, SunSoft Press. ISBN 0-13-172389-8
  • Pat Villani. Speciális WIN32 programozás: fájlok, szálak és folyamatszinkronizálás. Harpercollins Kiadó. ISBN 0-87930-563-0
  • Jim Beveridge, Robert Wiener. Többszálú alkalmazások a Win32-ben. Addison Wesley. ISBN 0-201-44234-5
  • Thuan Q. Pham, Pankaj K. Garg. Többszálú programozás Windows NT-vel. Prentice Hall. ISBN 0-13-120643-5
  • Len Dorfman, Marc J. Neuberger. Hatékony többszálú megoldás OS/2-ben. McGraw-Hill Osborne Media. ISBN 0-07-017841-0
  • Alan Burns, Andy Wellings. Egyidejűség az ADA-ban. Cambridge University Press. ISBN 0-521-62911-X
  • Uresh Vahalia. Unix belső: az új határok. Prentice Hall. ISBN 0-13-101908-2
  • Alan L. Dennis. .Net multithreading. Manning Publications Company. ISBN 1-930110-54-5
  • Tobin Titus, Fabio Claudio Ferracchiati, Srinivasa Sivakumar, Tejaswi Redkar, Sandra Gopikrishna. C# Threading kézikönyv. Peer információk. ISBN 1-86100-829-5
  • Tobin Titus, Fabio Claudio Ferracchiati, Srinivasa Sivakumar, Tejaswi Redkar, Sandra Gopikrishna. Visual Basic .Net Threading kézikönyv. Wrox Press. ISBN 1-86100-713-2

Linkek