Multithreading
Versiunea actuală a paginii nu a fost încă examinată de colaboratori experimentați și poate diferi semnificativ de
versiunea revizuită pe 16 martie 2016; verificările necesită
43 de modificări .
Multithreading ( eng. Multithreading ) este o proprietate a unei platforme (de exemplu, un sistem de operare , o mașină virtuală etc.) sau a unei aplicații , constând în faptul că un proces generat în sistemul de operare poate consta din mai multe fire de execuție care rulează " în paralel ”, apoi mănâncă fără o comandă prescrisă la timp . Pentru unele sarcini, această separare poate realiza o utilizare mai eficientă a resurselor computerului .
Astfel de fire sunt numite și fire de execuție (din engleză thread of execution ); numite uneori „fire” (traducere literală a firului englezesc ) sau informal „fire”.
Descriere
Esența multithreading-ului este cvasi-multitasking-ul la nivelul unui proces executabil, adică toate firele sunt executate în spațiul de adrese al procesului. În plus, toate firele de execuție dintr-un proces partajează nu numai un spațiu de adrese comun, ci și descriptori de fișier comun . Un proces care rulează are cel puțin un fir (master).
Multithreading (ca doctrină de programare ) nu trebuie confundat nici cu multitasking , fie cu multiprocessing , chiar dacă sistemele de operare care implementează multitasking tind să implementeze și multithreading.
Avantajele unei implementări multithreaded a unui anumit sistem față de unul multitasking includ următoarele:
- Simplificarea programului în unele cazuri prin utilizarea unui spațiu de adrese comun.
- Mai puțin timp petrecut pentru crearea unui fir în raport cu procesul.
Avantajele implementării cu mai multe fire a unui anumit sistem față de unul cu un singur thread includ următoarele:
- Simplificarea programului în unele cazuri, datorită eliminării mecanismelor de intercalare a execuției diferitelor subsarcini slab interconectate care necesită execuție simultană, într-un subsistem multithreading separat.
- Îmbunătățirea performanței procesului prin paralelizarea calculelor procesorului și a operațiilor I/O.
În cazul în care firele de execuție necesită o interacțiune relativ complexă între ele, pot apărea probleme de multitasking, cum ar fi blocajele.
Implementarea hardware
Pe un procesor convențional, gestionarea firelor este gestionată de sistemul de operare. Firul este executat până când apare o întrerupere hardware, are loc un apel de sistem sau până când expiră timpul alocat pentru acesta de sistemul de operare. După aceea, procesorul trece la codul sistemului de operare, care salvează starea firului de execuție (contextul său) sau trece la starea unui alt fir, căruia i se alocă și timp pentru execuție. Cu o astfel de multithreading, un număr suficient de mare de cicluri de procesor este cheltuit pe codul sistemului de operare care schimbă contextele. Dacă suportul pentru fire este implementat în hardware, atunci procesorul însuși va putea comuta între fire și, în cazul ideal, executa mai multe fire simultan pentru fiecare ciclu de ceas. Pentru sistemul de operare și utilizator, un astfel de procesor fizic va fi văzut ca procesoare logice multiple.
Există două forme de multithreading care pot fi implementate în procesoare din hardware:
Tipuri de implementare a firelor
- Un fir în spațiul utilizatorului. Fiecare proces are o tabelă de fire similară cu tabelul de procese ale nucleului. Dezavantajele includ:
- Nicio întrerupere a temporizatorului în cadrul aceluiași proces
- Când utilizați o solicitare de sistem de blocare pentru un proces, toate firele sale sunt blocate.
- Complexitatea implementării
- Flux în spațiul nucleului. Alături de tabelul de procese, există un tabel de fire în spațiul kernel.
- „Fibre” ( ing. fibre ). Mai multe fire de execuție în modul utilizator care se execută pe un singur fir de execuție în mod kernel. Un fir de execuție de spațiu kernel consumă resurse notabile, în primul rând memorie fizică și un interval de adrese în modul kernel pentru stiva de mod kernel. Prin urmare, a fost introdus conceptul de „fibră” - un fir ușor care rulează exclusiv în modul utilizator. Fiecare fir poate avea mai multe „fibre”.
Interacțiunea cu firele
Într-un mediu cu mai multe fire, există adesea sarcini care necesită suspendarea și reluarea unor fire în funcție de munca altora. În special, acestea sunt sarcini legate de prevenirea conflictelor de acces atunci când se utilizează aceleași date sau dispozitive din firele executabile paralele. Pentru a rezolva astfel de probleme, se folosesc obiecte speciale pentru interacțiunea firelor, cum ar fi excluderi reciproce (mutexuri), semafore, secțiuni critice, evenimente etc. Multe dintre aceste obiecte sunt obiecte nucleu și pot fi utilizate nu numai între firele de execuție ale aceluiași proces, ci și pentru interacțiunea dintre firele de execuție ale diferitelor procese.
- Un mutex este un obiect de sincronizare care este setat la o stare specială de semnalizare atunci când nu este ocupat de niciun fir. Un singur fir deține acest obiect în orice moment, de unde și numele unor astfel de obiecte (din engleză , reciproc exclusive access - mutually exclusive access) - accesul simultan la o resursă partajată este exclus. După toate acțiunile necesare, mutex-ul este eliberat, oferind altor fire acces la resursa partajată. Un obiect poate suporta captura recursivă a doua oară de către același fir, incrementând contorul fără a bloca firul și apoi necesitând mai multe eliberări. Aceasta este, de exemplu, secțiunea critică din Win32 . Cu toate acestea, există unele implementări care nu acceptă acest lucru și determină blocarea firului de execuție atunci când încearcă o captură recursivă. De exemplu, acesta este FAST_MUTEX în nucleul Windows.
- Secțiunile critice oferă o sincronizare similară cu mutexurile, cu excepția faptului că obiectele care reprezintă secțiunile critice sunt accesibile doar într-un singur proces. Evenimentele, mutexurile și semaforele pot fi, de asemenea, utilizate în firele de aplicare cu un singur proces, cu toate acestea, implementările de secțiuni critice în unele sisteme de operare (de exemplu, Windows NT) oferă un mecanism mai rapid și mai eficient [1] [2] pentru excluderea reciprocă . sincronizare - „obține” și „eliberare” din secțiunea critică sunt optimizate pentru cazul unui singur fir (fără dispută) pentru a evita orice apeluri de sistem care duc la nucleul OS.
- Semaforele sunt resurse disponibile care pot fi achiziționate de mai multe fire în același timp până când pool-ul de resurse este gol. Apoi firele suplimentare trebuie să aștepte până când cantitatea necesară de resurse este disponibilă din nou.
- Evoluții. Un obiect care stochează 1 bit de informații „semnalizat sau nu”, pe care sunt definite operațiunile „semnal”, „resetare la o stare nesemnalizată” și „așteptare”. Așteptarea unui eveniment semnalat este absența unei operațiuni cu continuarea imediată a execuției firului. Așteptarea unui eveniment nesemnalizat determină suspendarea execuției unui fir de execuție până când un alt fir de execuție (sau a doua fază a unui handler de întrerupere din nucleul sistemului de operare) semnalează evenimentul. Este posibil să așteptați mai multe evenimente în modurile „oricare” sau „toate”. De asemenea, este posibil să se creeze un eveniment care este resetat automat la o stare nesemnalizată după trezirea primului - și singurului - fir în așteptare (un astfel de obiect este folosit ca bază pentru implementarea obiectului „secțiune critică”). Folosit activ în MS Windows, atât în modul utilizator, cât și în modul kernel. Există un obiect similar în nucleul Linux numit kwait_queue.
- Variabile condiționale (condvars). Similar evenimentelor, dar nu sunt obiecte care ocupă memorie - se folosește doar adresa variabilei, conceptul de „conținut al variabilei” nu există, adresa unui obiect arbitrar poate fi folosită ca variabilă de condiție. Spre deosebire de evenimente, setarea unei variabile de condiție la o stare semnalată nu are consecințe dacă în prezent nu există fire de execuție în așteptare pentru variabilă. Setarea unui eveniment într-un caz similar implică stocarea stării „semnalizate” în cadrul evenimentului în sine, după care firele ulterioare care doresc să aștepte evenimentul continuă imediat execuția fără oprire. Pentru a folosi pe deplin un astfel de obiect, este necesară și operația „eliberați mutexul și așteptați variabila de condiție atomic”. Folosit activ în sistemele de operare asemănătoare UNIX . Discuțiile despre avantajele și dezavantajele evenimentelor și ale variabilelor de condiție sunt o parte importantă a discuțiilor despre avantajele și dezavantajele Windows și UNIX.
- Port de completare IO (IOCP). Implementat în nucleul sistemului de operare și accesibil prin apeluri de sistem, obiectul „coadă” cu operațiunile „pune structura la coada cozii” și „ia următoarea structură din capul cozii” - ultimul apel suspendă execuția al firului de execuție dacă coada este goală și până când niciun alt thread nu va efectua apelul put. Cea mai importantă caracteristică a IOCP este că structurile pot fi plasate în el nu numai printr-un apel de sistem explicit din modul utilizator, ci și implicit în interiorul nucleului OS, ca urmare a finalizării unei operațiuni I/O asincrone pe unul dintre fișiere. descriptori. Pentru a obține acest efect, trebuie să utilizați apelul de sistem „asociați un descriptor de fișier cu IOCP”. În acest caz, structura plasată în coadă conține codul de eroare al operației I/O, precum și, în cazul succesului acestei operațiuni, numărul de octeți efectiv introduși sau de ieșire. Implementarea portului de completare limitează, de asemenea, numărul de fire care se execută pe un singur procesor/nucleu după ce o structură este primită din coadă. Obiectul este specific MS Windows și permite procesarea cererilor de conexiune de intrare și a fragmentelor de date în software-ul server într-o arhitectură în care numărul de fire poate fi mai mic decât numărul de clienți (nu există nicio cerință de a crea un fir separat cu costuri de resurse pentru fiecare client nou).
- RESURSA. Un mutex care acceptă captura recursivă, cu semantică de captură partajată sau exclusivă. Semantică: un obiect poate fi fie liber, fie capturat de un număr arbitrar de fire într-o manieră partajată, fie dobândit de un singur fir într-o manieră exclusivă. Orice încercare de a face capturi care încalcă această regulă va determina blocarea firului de execuție până când obiectul este eliberat pentru a permite capturarea. Există și operațiuni de tip TryToAcquire - nu blochează niciodată firul de execuție, fie îl captează, fie (dacă este necesară blocarea) returnează FALSE fără a face nimic. Este folosit în nucleul Windows, în special în sistemele de fișiere - de exemplu, orice fișier de disc deschis de cineva corespunde structurii FCB, în care există 2 astfel de obiecte pentru sincronizarea accesului la dimensiunea fișierului. Una dintre ele - resursa IO de paginare - este capturată exclusiv numai în calea de tăiere a fișierului și asigură că în momentul tăierii fișierul nu are I/O activ din cache și maparea memoriei.
- protecția împotriva deteriorării . Un obiect semi-documentat (apelurile sunt în fișierele antet, dar nu în documentație) în nucleul Windows. Contor cu operațiuni „creștere”, „scădere” și „așteptați”. Așteptarea blochează firul până când operațiunile de decrementare reduc contorul la zero. În plus, operația de creștere poate eșua, iar prezența unui timp de așteptare activ în prezent face ca toate operațiunile de creștere să eșueze.
Critica terminologiei
Traducerea termenului englezesc thread ca „thread” într-un context legat de programare contrazice traducerea acestuia „thread” într-un context general al limbajului și creează, de asemenea, coliziuni cu termenul Data stream .
Cu toate acestea, termenul „flux” este asociat cu traduceri ale literaturii tehnice străine realizate în anii 1970 de editura Mir. În prezent, în „cercurile academice” (adică în manuale, materiale didactice, cursuri universitare, disertații etc.), este considerată o referință. Termenii „fir”, „fir” etc. sunt considerați jargon tehnic .
Vezi și
Literatură
- Kunle Olukotun. Chip Multiprocessor Architecture - Tehnici de îmbunătățire a debitului și a latenței. - Morgan and Claypool Publishers, 2007. - 154 p. — ISBN 159829122X . (Engleză)
- Mario Nemirovsky, Dean M. Tullsen. arhitectură multithreading. - Morgan and Claypool Publishers, 2013. - 1608458555 p. — ISBN 1608458555 . (Engleză)
Note
- ↑ Jeffrey Richter . „Jeffrey Richter. Windows pentru profesioniști. Construirea de aplicații WIN32 eficiente, adaptate specificului Windows pe 64 de biți. 2001
- ↑ MSDN http://msdn.microsoft.com/en-us/library/ms682530%28VS.85%29.aspx Arhivat la 23 decembrie 2011 la Wayback Machine
Link -uri