Multitrådning i C # med uppgifter

De dataprogramering termen "tråd" är en förkortning för körningstråd, där en processor följer en specificerad sökväg genom din kod. Konceptet att följa mer än en tråd åt gången introducerar ämnet multi-tasking och multi-threading.

En applikation har en eller flera processer i den. Tänk på en process som ett program som körs på din dator. Nu har varje process en eller flera trådar. En spelapplikation kan ha en tråd för att ladda resurser från disken, en annan för att göra AI och en annan för att köra spelet som en server.

I .NET / Windows fördelar operativsystemet processortid till en tråd. Varje tråd håller reda på undantagshanterare och den prioritet som den kör, och den har någonstans att spara trådkontexten tills den körs. Trådkontext är den information som tråden behöver återuppta.

Flera uppgifter med trådar

Trådar tar lite minne och det tar lite tid att skapa dem, så vanligtvis vill du inte använda många. Kom ihåg att de tävlar om processortid. Om din dator har flera CPU: er kan Windows eller .NET köra varje tråd på en annan CPU, men om flera trådar körs på samma CPU, då är det bara en som kan vara aktiv åt gången och växla trådar tar tid.

instagram viewer

CPU kör en tråd för några miljoner instruktioner, och sedan byter den till en annan tråd. Alla CPU-register, nuvarande programmets exekveringspunkt och stack måste sparas någonstans för den första tråden och sedan återställas från någon annanstans för nästa tråd.

Skapa en tråd

I namnområdet System. Threading, hittar du trådtypen. Konstruktorgängan (ThreadStart) skapar en instans av en tråd. Men på senare tid C # kod, är det mer sannolikt att passera i ett lambda-uttryck som kallar metoden med några parametrar.

Om du är osäker på lambda-uttryck, det kan vara värt att kolla in LINQ.

Här är ett exempel på en tråd som skapas och startas:

använder System;
använder System. gängning;
namnutrymme ex1
{
klassprogram
{
public static void Writ1 ()
{
Trösta. Skriv ('1');
Tråd. Sömn (500);
}
static void Main (string [] args)
{
var uppgift = ny tråd (skriv1);
uppgift. Start() ;
för (var i = 0; i <10; i ++)
{
Trösta. Skriv ('0');
Trösta. Skriv (uppgift. Lever? 'A': 'D');
Tråd. Sömn (150);
}
Trösta. ReadKey ();
}
}
}

Allt detta exempel gör är att skriva "1" till konsolen. Huvudtråden skriver en "0" till konsolen 10 gånger, varje gång följt av en "A" eller "D" beroende på om den andra tråden fortfarande är levande eller död.

Den andra tråden löper bara en gång och skriver en "1." Efter halv sekunders försening i tråden Writ1 () är tråden avslutad och uppgiften. IsAlive i huvudslingan returnerar nu "D."

Trådpool och uppgiftsparallellbibliotek

Istället för att skapa din egen tråd, om du verkligen behöver göra det, använd en trådpool. Från .NET 4.0 har vi tillgång till Task Parallel Library (TPL). Som i föregående exempel behöver vi återigen lite LINQ, och ja, det är allt lambda-uttryck.

Uppgifterna använder Gängbassäng bakom kulisserna men utnyttjar trådarna bättre beroende på antalet som används.

Huvudobjektet i TPL är en uppgift. Detta är en klass som representerar en asynkron operation. Det vanligaste sättet att börja saker som körs är med uppgiften. Fabrik. StartNy som i:

Uppgift. Fabrik. StartNew (() => DoSomething ());

Där DoSomething () är metoden som körs. Det är möjligt att skapa en uppgift och inte få den att köras omedelbart. I så fall använder du bara Uppgift så här:

var t = new Uppgift (() => Konsol. WriteLine ( "Hello"));
...
t. Start();

Det startar inte tråden förrän .Start () heter. I exemplet nedan finns fem uppgifter.

använder System;
använder System. gängning;
använder System. Gäng. uppgifter;
namnutrymme ex1
{
klassprogram
{
public static void Writ1 (int i)
{
Trösta. Skriv (i);
Tråd. Sömn (50);
}
static void Main (string [] args)
{
för (var i = 0; i <5; i ++)
{
var värde = i;
var runningTask = Uppgift. Fabrik. StartNew (() => Writ1 (värde));
}
Trösta. ReadKey ();
}
}
}

Kör det så får du siffrorna 0 till 4 i en slumpmässig ordning som 03214. Det beror på att ordningen för utförande av uppgifter bestäms av .NET.

Du kanske undrar varför var-värdet = i behövs. Försök ta bort den och ringa Skriv (i) så ser du något oväntat som 55555. Varför är detta? Det beror på att uppgiften visar värdet på i vid den tidpunkt då uppgiften körs, inte när uppgiften skapades. Genom att skapa en ny variabel varje gång i slingan lagras och plockas upp var och en av de fem värdena.