Rezerva de fire

De la Wikipedia, enciclopedia liberă.
Salt la navigare Salt la căutare

Un pool de fire de programare se referă la un manager de fire de software utilizat pentru a optimiza și simplifica utilizarea threadurilor în cadrul unui program.

Funcționare generală

Schema generică de funcționare a unui pool de fire: sarcinile în așteptare (puncte albastre) sunt executate în paralel de fire (cutii verzi). Punctele galbene indică sarcinile finalizate.

În timpul scrierii unui program este posibil, cu diferite metode, să trimiteți sarcini (sau sarcini) care urmează să fie executate în grupul de fire. Va fi sarcina grupului de fire să încredințeze sarcina unui fir.
Grupurile de fire au de obicei o coadă internă de sarcini în așteptare și un anumit număr de fire cu care să le ruleze. În funcție de implementarea grupului de fire, se utilizează reguli diferite pentru a decide ce sarcină să ruleze mai întâi, câte fire active să se utilizeze și câte fire de așteptat când coada de activități este goală.

Un concept recurent în grupurile de fire este reutilizarea firelor: un fir este utilizat de mai multe ori pentru diferite sarcini în timpul ciclului său de viață. Acest lucru scade cheltuielile generale de creare a firelor și crește performanța programului care utilizează grupul de fire. Reutilizarea threadurilor nu este o regulă, dar este unul dintre principalele motive care determină un programator să utilizeze un pool de fire în aplicațiile sale.

Implementare și funcționare

O implementare a grupului de fire de lucru constă, de obicei, dintr-o coadă de activități și un set de fire de execuție, ambele fiind inițial goale.
Parametrii tipici de funcționare sunt după cum urmează:

  • lungimea cozii de sarcini (poate fi practic infinită);
  • numărul de fire care rămân vii în absența unei sarcini;
  • numărul maxim de fire care pot fi create (poate fi practic infinit [1] ).

Odată ce grupul de fire a fost inițializat, este posibil să se solicite ca firele minime să fie create imediat, adică cele care ar rămâne în viață chiar și în absența sarcinilor [2] . Această funcție este deseori exploatată dacă se așteaptă o utilizare intensivă a grupului de fire și se dorește o anumită reactivitate în executarea sarcinilor.
În acest moment este posibil să solicitați grupului de fire să execute sarcini: orice număr de sarcini independente [3] pot fi adăugate la coada internă, fără a întrerupe fluxul principal al programului care utilizează grupul de fire.
Dacă coada de sarcini este infinită, sarcina este întotdeauna acceptată, altfel ar putea fi respinsă dacă coada este plină. Ori de câte ori grupul de fire acceptă o sarcină, verifică dacă există fire libere în așteptare și, dacă da, atribuie sarcina unuia dintre ele. Dacă nu există fire libere, verificați dacă este posibil să creați unul nou: dacă a fost atins numărul maxim de fire, sarcina este pusă în coadă, altfel se creează unul nou și sarcina i se atribuie.
În unele implementări, coada de sarcini poate fi prioritară sau inexistentă (adică are lungimea 0). În primul caz, extragerea unei sarcini din coadă depinde de prioritatea acesteia, în al doilea caz sarcina este acceptată numai dacă poate fi executată imediat.
Când un fir de execuție a terminat executarea unei sarcini, acesta trece la următoarea sarcină din coadă, dacă există. Dacă nu există sarcini în așteptare, firul în sine verifică dacă este în surplus: dacă există mai multe fire active decât cele minime (care pot rămâne în așteptarea sarcinilor), acesta se termină și își termină ciclul de viață, altfel se pune în așteptare.
Un pool de fire are, de asemenea, o metodă de așteptare a sarcinilor atribuite pentru a termina sau a închide forțat toate thread-urile care sunt încă în viață, eliminând sarcinile aflate în coadă.

Implementări diferite ar putea prezice comportamente diferite, dar schema generică este cea prezentată până acum.

Beneficii

Utilizarea unui pool de fire de execuție care este nativ sau implementat într-o anumită arhitectură are ca rezultat o creștere a performanței de către aplicațiile care îl exploatează: pool-urile de fire de lucru optimizează utilizarea memoriei și a procesorului, reducând cheltuielile generale de gestionare a firelor. Cu toate acestea, este posibil să adaptați grupul de fire la nevoile dvs. dacă parametrii standard nu sunt satisfăcătoare. Mai mult, paralelizarea unor operații duce la o optimizare a resurselor și la o creștere a vitezei în îndeplinirea sarcinilor, chiar dacă introduce alte tipuri de probleme, cum ar fi accesul exclusiv la resursele partajate (problema stării rasei ).
Un pool de fire vă permite să vă simplificați codul și să evitați să vă faceți griji cu privire la crearea și terminarea firelor, mai ales atunci când nu știți câte.

Arhitecturi care utilizează pool-uri de fire

Grupurile de fire sunt utilizate în aproape toate aplicațiile de server, unde este nevoie să gestioneze mai multe cereri simultane de la un număr nespecificat de clienți.
Cu toate acestea, multe alte aplicații au nevoie de fiecare sarcină pentru a rula instantaneu sau pentru a avea mai mult control asupra firului care îl execută: în acest caz, grupul de fire nu este cea mai bună alegere.

Limbi care implementează pool-uri de fire

Exemple

Mai jos sunt câteva exemple de utilizare a grupurilor de fire în principalele limbaje de programare care le acceptă în mod nativ.

Java

 import java.util.concurrent.Executori ;
import java.util.concurrent.ExecutorService ;

public class Exemplu {
	void main (String [] args) {public static
		// Creez un nou pool de fire.
		ExecutorService threadpool = Executori . newCachedThreadPool ();

		// Am pus la coadă sarcina
		filet . executa (
	            // Acest obiect reprezintă sarcina.
                    nou Runnable () {
			public void run () {
				Sistem . afară . println ( "Sunt executat de grupul de fire." );
			}
		});

                Sistem . afară . println ( "Firul principal continuă și apoi rămâne în așteptare." );
		filet . închidere ();
                Sistem . afară . println ( " Ieșirea firului principal." );
	}
}

Visual Basic .NET

 Sistemul de importuri 
Sistemul de importuri

Exemplu de clasă publică

	< MTAThread > _ 
	Sub Main Public Shared () 
		'Am pus la coadă sarcina.
		ThreadPool . QueueUserWorkItem ( _ 
		        New WaitCallback ( AddressOf ThreadProc ) _ 
		        )

		Consolă . WriteLine ( „Firul principal continuă și apoi rămâne în așteptare.” ) 
		Fir . Somn ( 1000 )

		Consolă . WriteLine (  Ieșirea firului principal”. ) 
	Sfârșitul Sub

	„Această procedură reprezintă sarcina.
	Partajat sub ThreadProc ( stateInfo ca obiect ) 
		Consolă . WriteLine ( „Sunt executat de grupul de fire.” ) 
	Sfârșitul Sub 
Clasa de sfârșit

C # .NET

 utilizarea sistemului ; 
folosind System.Threading ; 
public class Exemplu { 
	public static void Main () { 
		// Am pus la coadă sarcina.
		ThreadPool . QueueUserWorkItem ( nou WaitCallback ( ThreadProc ));

		Consolă . WriteLine ( "Firul principal continuă și apoi rămâne în așteptare." );
		Fir . Somn ( 1000 );

		Consolă . WriteLine ( " Ieșirea firului principal." ); 
	}

	// Această procedură reprezintă sarcina.
	static nul ThreadProc ( Object stateInfo ) { 
		Consolă . WriteLine ( „Sunt executat de grupul de fire.” ); 
	} 
}

C ++ .NET

 folosind sistemul de spațiu de nume ; 
folosind spațiul de nume System :: Threading ; 
clasa ref Exemplu 
{ 
public :

	// Această procedură reprezintă sarcina.
	static nul ThreadProc ( Object ^ stateInfo ) 
	{ 
		Console :: WriteLine ( "Sunt executat de grupul de fire." ); 
	}

};

int main () 
{
	// Am pus la coadă sarcina.
	ThreadPool :: QueueUserWorkItem (gcnew WaitCallback (Exemplu :: ThreadProc)); 
	Console :: WriteLine ( "Firul principal continuă și apoi rămâne în așteptare." );

	Fir :: Somn ( 1000 ); 
	Consolă :: WriteLine ( " Ieșirea firului principal." ); 
	retur 0 ; 
}

J # .NET

sistem de import . * ; 
import System.Threading. * ; 
import System.Threading.Thread ;

public class Exemplu 
{ 
	public static void main (String [] args) 
	{ 
		// Am pus la coadă sarcina.
		ThreadPool . QueueUserWorkItem ( nou WaitCallback ( ThreadProc )); 
		Consolă . WriteLine ( "Firul principal continuă și apoi rămâne în așteptare." );

		Fir . Somn ( 1000 ); 
		Consolă . WriteLine ( " Ieșirea firului principal." ); 
	} // principal

	// Această procedură reprezintă sarcina.
	static nul ThreadProc ( Object stateInfo ) 
	{
		Consolă . WriteLine ( „Sunt executat de grupul de fire.” ); 
	} // ThreadProc
} // Exemplu

Notă

  1. ^ Nu este recomandat să nu se limiteze acest număr, deoarece un număr infinit învinge natura grupului de fire, care este de a optimiza utilizarea firelor.
  2. ^ Este posibil să nu existe un număr minim de fire pentru a rămâne activ.
  3. ^ Nu există garanții cu privire la momentul în care o sarcină va fi executată și acest lucru face periculoasă inserarea de sarcini care depind una de cealaltă, deoarece ar putea bloca coada (generând un blocaj )

Elemente conexe

linkuri externe

Informatică Portal IT : accesați intrările Wikipedia care se ocupă cu IT