Poznámka
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Knihovna paralelních úkolů (TPL) je založena na konceptu úkolu, který představuje asynchronní operaci. Úloha se některým způsobem podobá vláknu nebo ThreadPool pracovní položce, ale na vyšší úrovni abstrakce. Termín paralelismus úkolů odkazuje na jednu nebo více nezávislých úloh spuštěných souběžně. Úkoly poskytují dvě hlavní výhody:
Efektivnější a škálovatelné využití systémových prostředků
Na pozadí jsou úkoly zařazeny do fronty ThreadPool, která byla vylepšena pomocí algoritmů, které určují a upravují počet vláken. Tyto algoritmy poskytují vyrovnávání zatížení pro maximalizaci propustnosti. Díky tomuto procesu jsou úlohy poměrně jednoduché a můžete je vytvořit, abyste umožnili jemně odstupňovaný paralelismus.
Více programové kontroly, než je možné s vláknem nebo pracovní položkou.
Úlohy a architektura postavené na nich poskytují bohatou sadu rozhraní API, která podporují čekání, zrušení, pokračování, robustní zpracování výjimek, podrobný stav, vlastní plánování a další.
Z obou důvodů je TPL upřednostňovaným rozhraním API pro psaní vícevláknového, asynchronního a paralelního kódu v .NET.
Implicitní vytváření a spouštění úloh
Metoda Parallel.Invoke poskytuje pohodlný způsob, jak souběžně spouštět libovolný počet příkazů. Stačí pro každou pracovní položku předat delegáta Action. Nejjednodušší způsob, jak tyto delegáty vytvořit, je použít výrazy lambda. Výraz lambda může buď volat pojmenovanou metodu, nebo poskytnout přímo kód. Následující příklad ukazuje základní Invoke volání, které vytvoří a spustí dva úlohy, které běží souběžně. První úkol je reprezentován výrazem lambda, který volá metodu s názvem DoSomeWork
a druhý úkol je reprezentován výrazem lambda, který volá metodu s názvem DoSomeOtherWork
.
Poznámka
Tato dokumentace používá výrazy lambda k definování delegátů v TPL. Pokud nejste obeznámeni s Výrazy Lambda v jazycích C# nebo Visual Basic, přečtěte si Výrazy Lambda v PLINQ a TPL.
Parallel.Invoke(() => DoSomeWork(), () => DoSomeOtherWork());
Parallel.Invoke(Sub() DoSomeWork(), Sub() DoSomeOtherWork())
Poznámka
Počet instancí Task, které jsou na pozadí vytvořeny pomocí Invoke, nemusí nutně odpovídat počtu poskytnutých delegátů. TPL může využívat různé optimalizace, zejména u velkého počtu delegátů.
Další informace naleznete v tématu Postupy: Použití Parallel.Invoke ke spouštění paralelních operací.
Pokud chcete mít větší kontrolu nad prováděním úkolů nebo vrátit hodnotu z úkolu, musíte pracovat s objekty Task explicitněji.
Explicitní vytváření a spouštění úloh
Úkol, který nevrací hodnotu, je reprezentován třídou System.Threading.Tasks.Task. Úkol, který vrací hodnotu, je reprezentován třídou System.Threading.Tasks.Task<TResult>, která dědí z Task. Objekt úlohy zpracovává podrobnosti o infrastruktuře a poskytuje metody a vlastnosti, které jsou přístupné z volajícího vlákna po celou dobu životnosti úlohy. K vlastnosti Status úkolu můžete například přistupovat kdykoli a zjistit, jestli byl spuštěn, dokončen, zrušen nebo vyvolal výjimku. Stav je reprezentován TaskStatus výčtem.
Když vytvoříte úkol, udělíte mu uživatelský delegát, který zapouzdřuje kód pro spuštění úkolu. Delegáta lze vyjádřit jako pojmenovaný delegát, anonymní metodu nebo výraz lambda. Výrazy lambda mohou obsahovat volání pojmenované metody, jak je znázorněno v následujícím příkladu. Příklad obsahuje volání metody Task.Wait, aby se zajistilo, že úloha dokončí provádění před ukončením aplikace v režimu konzoly.
using System;
using System.Threading;
using System.Threading.Tasks;
public class Lambda
{
public static void Main()
{
Thread.CurrentThread.Name = "Main";
// Create a task and supply a user delegate by using a lambda expression.
Task taskA = new Task( () => Console.WriteLine("Hello from taskA."));
// Start the task.
taskA.Start();
// Output a message from the calling thread.
Console.WriteLine($"Hello from thread '{Thread.CurrentThread.Name}'.");
taskA.Wait();
}
}
// The example displays output as follows:
// Hello from thread 'Main'.
// Hello from taskA.
// or
// Hello from taskA.
// Hello from thread 'Main'.
Imports System.Threading
Namespace Lambda
Module Example
Public Sub Main()
Thread.CurrentThread.Name = "Main"
' Create a task and supply a user delegate by using a lambda expression.
Dim taskA = New Task(Sub() Console.WriteLine("Hello from taskA."))
' Start the task.
taskA.Start()
' Output a message from the calling thread.
Console.WriteLine("Hello from thread '{0}'.",
Thread.CurrentThread.Name)
taskA.Wait()
End Sub
End Module
' The example displays output like the following:
' Hello from thread 'Main'.
' Hello from taskA.
End Namespace
Pomocí metod Task.Run můžete také vytvořit a spustit úlohu v jedné operaci. Ke správě úlohy používají Run metody výchozí plánovač úloh bez ohledu na to, který plánovač úloh je přidružen k aktuálnímu vláknu. Run metody jsou upřednostňovaným způsobem vytváření a spouštění úkolů, pokud není potřeba mít větší kontrolu nad vytvořením a plánováním úkolu.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Run;
public class Example
{
public static void Main()
{
Thread.CurrentThread.Name = "Main";
// Define and run the task.
Task taskA = Task.Run( () => Console.WriteLine("Hello from taskA."));
// Output a message from the calling thread.
Console.WriteLine($"Hello from thread '{Thread.CurrentThread.Name}'.");
taskA.Wait();
}
}
// The example displays output as follows:
// Hello from thread 'Main'.
// Hello from taskA.
// or
// Hello from taskA.
// Hello from thread 'Main'.
Imports System.Threading
Namespace Run
Module Example
Public Sub Main()
Thread.CurrentThread.Name = "Main"
Dim taskA As Task = Task.Run(Sub() Console.WriteLine("Hello from taskA."))
' Output a message from the calling thread.
Console.WriteLine("Hello from thread '{0}'.",
Thread.CurrentThread.Name)
taskA.Wait()
End Sub
End Module
' The example displays output like the following:
' Hello from thread 'Main'.
' Hello from taskA.
End Namespace
Pomocí metody TaskFactory.StartNew můžete také vytvořit a spustit úlohu v jedné operaci. Jak je znázorněno v následujícím příkladu, můžete tuto metodu použít v těchto případech:
Vytváření a plánování nemusí být oddělené a vyžadujete další možnosti vytváření úkolů nebo použití konkrétního plánovače.
Musíte předat do úlohy další stav, který můžete načíst prostřednictvím její vlastnosti Task.AsyncState.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskIntro;
class CustomData
{
public long CreationTime;
public int Name;
public int ThreadNum;
}
public class AsyncState
{
public static void Main()
{
Task[] taskArray = new Task[10];
for (int i = 0; i < taskArray.Length; i++)
{
taskArray[i] = Task.Factory.StartNew((Object obj) =>
{
CustomData data = obj as CustomData;
if (data == null) return;
data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
},
new CustomData() { Name = i, CreationTime = DateTime.Now.Ticks });
}
Task.WaitAll(taskArray);
foreach (var task in taskArray)
{
var data = task.AsyncState as CustomData;
if (data != null)
Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
data.Name, data.CreationTime, data.ThreadNum);
}
}
}
// The example displays output like the following:
// Task #0 created at 635116412924597583, ran on thread #3.
// Task #1 created at 635116412924607584, ran on thread #4.
// Task #2 created at 635116412924607584, ran on thread #4.
// Task #3 created at 635116412924607584, ran on thread #4.
// Task #4 created at 635116412924607584, ran on thread #3.
// Task #5 created at 635116412924607584, ran on thread #3.
// Task #6 created at 635116412924607584, ran on thread #4.
// Task #7 created at 635116412924607584, ran on thread #4.
// Task #8 created at 635116412924607584, ran on thread #3.
// Task #9 created at 635116412924607584, ran on thread #4.
Imports System.Threading
Namespace AsyncState
Class CustomData
Public CreationTime As Long
Public Name As Integer
Public ThreadNum As Integer
End Class
Module Example
Public Sub Main()
Dim taskArray(9) As Task
For i As Integer = 0 To taskArray.Length - 1
taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
Dim data As CustomData = TryCast(obj, CustomData)
If data Is Nothing Then Return
data.ThreadNum = Environment.CurrentManagedThreadId
End Sub,
New CustomData With {.Name = i, .CreationTime = Date.Now.Ticks})
Next
Task.WaitAll(taskArray)
For Each task In taskArray
Dim data = TryCast(task.AsyncState, CustomData)
If data IsNot Nothing Then
Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
data.Name, data.CreationTime, data.ThreadNum)
End If
Next
End Sub
End Module
' The example displays output like the following:
' Task #0 created at 635116412924597583, ran on thread #3.
' Task #1 created at 635116412924607584, ran on thread #4.
' Task #2 created at 635116412924607584, ran on thread #4.
' Task #3 created at 635116412924607584, ran on thread #4.
' Task #4 created at 635116412924607584, ran on thread #3.
' Task #5 created at 635116412924607584, ran on thread #3.
' Task #6 created at 635116412924607584, ran on thread #4.
' Task #7 created at 635116412924607584, ran on thread #4.
' Task #8 created at 635116412924607584, ran on thread #3.
' Task #9 created at 635116412924607584, ran on thread #4.
End Namespace
Task a Task<TResult> zpřístupňují statickou vlastnost Factory, která vrací výchozí instanci TaskFactory, abyste mohli metodu volat jako Task.Factory.StartNew()
. Také v následujícím příkladu, protože úkoly jsou typu System.Threading.Tasks.Task<TResult>, mají veřejnou vlastnost Task<TResult>.Result, která obsahuje výsledek výpočtu. Úlohy běží asynchronně a můžou být dokončeny v libovolném pořadí. Pokud je vlastnost Result přistoupena před dokončením výpočtu, vlastnost blokuje volající vlákno, dokud nebude hodnota k dispozici.
using System;
using System.Threading.Tasks;
public class Result
{
public static void Main()
{
Task<Double>[] taskArray = { Task<Double>.Factory.StartNew(() => DoComputation(1.0)),
Task<Double>.Factory.StartNew(() => DoComputation(100.0)),
Task<Double>.Factory.StartNew(() => DoComputation(1000.0)) };
var results = new Double[taskArray.Length];
Double sum = 0;
for (int i = 0; i < taskArray.Length; i++) {
results[i] = taskArray[i].Result;
Console.Write("{0:N1} {1}", results[i],
i == taskArray.Length - 1 ? "= " : "+ ");
sum += results[i];
}
Console.WriteLine($"{sum:N1}");
}
private static Double DoComputation(Double start)
{
Double sum = 0;
for (var value = start; value <= start + 10; value += .1)
sum += value;
return sum;
}
}
// The example displays the following output:
// 606.0 + 10,605.0 + 100,495.0 = 111,706.0
Namespace Result
Module Example
Public Sub Main()
Dim taskArray() = {Task(Of Double).Factory.StartNew(Function() DoComputation(1.0)),
Task(Of Double).Factory.StartNew(Function() DoComputation(100.0)),
Task(Of Double).Factory.StartNew(Function() DoComputation(1000.0))}
Dim results(taskArray.Length - 1) As Double
Dim sum As Double
For i As Integer = 0 To taskArray.Length - 1
results(i) = taskArray(i).Result
Console.Write("{0:N1} {1}", results(i),
If(i = taskArray.Length - 1, "= ", "+ "))
sum += results(i)
Next
Console.WriteLine("{0:N1}", sum)
End Sub
Private Function DoComputation(start As Double) As Double
Dim sum As Double
For value As Double = start To start + 10 Step .1
sum += value
Next
Return sum
End Function
End Module
' The example displays the following output:
' 606.0 + 10,605.0 + 100,495.0 = 111,706.0
End Namespace
Další informace naleznete v tématu Jak vrátit hodnotu z úlohy.
Když k vytvoření delegáta použijete výraz lambda, budete mít přístup ke všem proměnným, které jsou viditelné v tomto okamžiku ve zdrojovém kódu. V některých případech, zejména uvnitř smyček, lambda nezachytí proměnnou očekávaným způsobem. Zachycuje pouze odkaz na proměnnou, nikoli její hodnotu, protože se proměnná mění po každé iteraci. Následující příklad ukazuje problém. Předá čítač smyčky lambda výrazu, který instanciuje objekt CustomData
a použije čítač smyčky jako identifikátor tohoto objektu. Jak ukazuje výstup z příkladu, každý objekt CustomData
má identický identifikátor.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Example.Iterations;
class CustomData
{
public long CreationTime;
public int Name;
public int ThreadNum;
}
public class IterationTwo
{
public static void Main()
{
// Create the task object by using an Action(Of Object) to pass in the loop
// counter. This produces an unexpected result.
Task[] taskArray = new Task[10];
for (int i = 0; i < taskArray.Length; i++) {
taskArray[i] = Task.Factory.StartNew( (Object obj) => {
var data = new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks};
data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"Task #{data.Name} created at {data.CreationTime} on thread #{data.ThreadNum}.");
},
i );
}
Task.WaitAll(taskArray);
}
}
// The example displays output like the following:
// Task #10 created at 635116418427727841 on thread #4.
// Task #10 created at 635116418427737842 on thread #4.
// Task #10 created at 635116418427737842 on thread #4.
// Task #10 created at 635116418427737842 on thread #4.
// Task #10 created at 635116418427737842 on thread #4.
// Task #10 created at 635116418427737842 on thread #4.
// Task #10 created at 635116418427727841 on thread #3.
// Task #10 created at 635116418427747843 on thread #3.
// Task #10 created at 635116418427747843 on thread #3.
// Task #10 created at 635116418427737842 on thread #4.
Imports System.Threading
Namespace IterationsTwo
Class CustomData
Public CreationTime As Long
Public Name As Integer
Public ThreadNum As Integer
End Class
Module Example
Public Sub Main()
' Create the task object by using an Action(Of Object) to pass in the loop
' counter. This produces an unexpected result.
Dim taskArray(9) As Task
For i As Integer = 0 To taskArray.Length - 1
taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
Dim data As New CustomData With {.Name = i, .CreationTime = Date.Now.Ticks}
data.ThreadNum = Environment.CurrentManagedThreadId
Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
data.Name, data.CreationTime, data.ThreadNum)
End Sub,
i)
Next
Task.WaitAll(taskArray)
End Sub
End Module
' The example displays output like the following:
' Task #10 created at 635116418427727841 on thread #4.
' Task #10 created at 635116418427737842 on thread #4.
' Task #10 created at 635116418427737842 on thread #4.
' Task #10 created at 635116418427737842 on thread #4.
' Task #10 created at 635116418427737842 on thread #4.
' Task #10 created at 635116418427737842 on thread #4.
' Task #10 created at 635116418427727841 on thread #3.
' Task #10 created at 635116418427747843 on thread #3.
' Task #10 created at 635116418427747843 on thread #3.
' Task #10 created at 635116418427737842 on thread #4.
End Namespace
K hodnotě každé iterace můžete získat přístup zadáním objektu stavu úkolu prostřednictvím jeho konstruktoru. Následující příklad upraví předchozí příklad použitím čítače smyčky při vytváření objektu CustomData
, který se poté předá výrazu lambda. Jak ukazuje výstup z příkladu, každý objekt CustomData
teď má jedinečný identifikátor založený na hodnotě čítače smyčky v době vytvoření instance objektu.
using System;
using System.Threading;
using System.Threading.Tasks;
class CustomData
{
public long CreationTime;
public int Name;
public int ThreadNum;
}
public class IterationOne
{
public static void Main()
{
// Create the task object by using an Action(Of Object) to pass in custom data
// to the Task constructor. This is useful when you need to capture outer variables
// from within a loop.
Task[] taskArray = new Task[10];
for (int i = 0; i < taskArray.Length; i++) {
taskArray[i] = Task.Factory.StartNew( (Object obj ) => {
CustomData data = obj as CustomData;
if (data == null)
return;
data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"Task #{data.Name} created at {data.CreationTime} on thread #{data.ThreadNum}.");
},
new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks} );
}
Task.WaitAll(taskArray);
}
}
// The example displays output like the following:
// Task #0 created at 635116412924597583 on thread #3.
// Task #1 created at 635116412924607584 on thread #4.
// Task #3 created at 635116412924607584 on thread #4.
// Task #4 created at 635116412924607584 on thread #4.
// Task #2 created at 635116412924607584 on thread #3.
// Task #6 created at 635116412924607584 on thread #3.
// Task #5 created at 635116412924607584 on thread #4.
// Task #8 created at 635116412924607584 on thread #4.
// Task #7 created at 635116412924607584 on thread #3.
// Task #9 created at 635116412924607584 on thread #4.
Imports System.Threading
Namespace IterationsOne
Class CustomData
Public CreationTime As Long
Public Name As Integer
Public ThreadNum As Integer
End Class
Module Example
Public Sub Main()
' Create the task object by using an Action(Of Object) to pass in custom data
' to the Task constructor. This is useful when you need to capture outer variables
' from within a loop.
Dim taskArray(9) As Task
For i As Integer = 0 To taskArray.Length - 1
taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
Dim data As CustomData = TryCast(obj, CustomData)
If data Is Nothing Then Return
data.ThreadNum = Environment.CurrentManagedThreadId
Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
data.Name, data.CreationTime, data.ThreadNum)
End Sub,
New CustomData With {.Name = i, .CreationTime = Date.Now.Ticks})
Next
Task.WaitAll(taskArray)
End Sub
End Module
' The example displays output like the following:
' Task #0 created at 635116412924597583 on thread #3.
' Task #1 created at 635116412924607584 on thread #4.
' Task #3 created at 635116412924607584 on thread #4.
' Task #4 created at 635116412924607584 on thread #4.
' Task #2 created at 635116412924607584 on thread #3.
' Task #6 created at 635116412924607584 on thread #3.
' Task #5 created at 635116412924607584 on thread #4.
' Task #8 created at 635116412924607584 on thread #4.
' Task #7 created at 635116412924607584 on thread #3.
' Task #9 created at 635116412924607584 on thread #4.
End Namespace
Tento stav se předává jako argument delegátu úkolu a lze k němu přistupovat z objektu úkolu pomocí vlastnosti Task.AsyncState. Následující příklad je variantou předchozího příkladu. Používá vlastnost AsyncState k zobrazení informací o objektech CustomData
předaných výrazu lambda.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskIntro;
class CustomData
{
public long CreationTime;
public int Name;
public int ThreadNum;
}
public class AsyncState
{
public static void Main()
{
Task[] taskArray = new Task[10];
for (int i = 0; i < taskArray.Length; i++)
{
taskArray[i] = Task.Factory.StartNew((Object obj) =>
{
CustomData data = obj as CustomData;
if (data == null) return;
data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
},
new CustomData() { Name = i, CreationTime = DateTime.Now.Ticks });
}
Task.WaitAll(taskArray);
foreach (var task in taskArray)
{
var data = task.AsyncState as CustomData;
if (data != null)
Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
data.Name, data.CreationTime, data.ThreadNum);
}
}
}
// The example displays output like the following:
// Task #0 created at 635116412924597583, ran on thread #3.
// Task #1 created at 635116412924607584, ran on thread #4.
// Task #2 created at 635116412924607584, ran on thread #4.
// Task #3 created at 635116412924607584, ran on thread #4.
// Task #4 created at 635116412924607584, ran on thread #3.
// Task #5 created at 635116412924607584, ran on thread #3.
// Task #6 created at 635116412924607584, ran on thread #4.
// Task #7 created at 635116412924607584, ran on thread #4.
// Task #8 created at 635116412924607584, ran on thread #3.
// Task #9 created at 635116412924607584, ran on thread #4.
Imports System.Threading
Namespace AsyncState
Class CustomData
Public CreationTime As Long
Public Name As Integer
Public ThreadNum As Integer
End Class
Module Example
Public Sub Main()
Dim taskArray(9) As Task
For i As Integer = 0 To taskArray.Length - 1
taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
Dim data As CustomData = TryCast(obj, CustomData)
If data Is Nothing Then Return
data.ThreadNum = Environment.CurrentManagedThreadId
End Sub,
New CustomData With {.Name = i, .CreationTime = Date.Now.Ticks})
Next
Task.WaitAll(taskArray)
For Each task In taskArray
Dim data = TryCast(task.AsyncState, CustomData)
If data IsNot Nothing Then
Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
data.Name, data.CreationTime, data.ThreadNum)
End If
Next
End Sub
End Module
' The example displays output like the following:
' Task #0 created at 635116412924597583, ran on thread #3.
' Task #1 created at 635116412924607584, ran on thread #4.
' Task #2 created at 635116412924607584, ran on thread #4.
' Task #3 created at 635116412924607584, ran on thread #4.
' Task #4 created at 635116412924607584, ran on thread #3.
' Task #5 created at 635116412924607584, ran on thread #3.
' Task #6 created at 635116412924607584, ran on thread #4.
' Task #7 created at 635116412924607584, ran on thread #4.
' Task #8 created at 635116412924607584, ran on thread #3.
' Task #9 created at 635116412924607584, ran on thread #4.
End Namespace
ID úkolu
Každý úkol obdrží celočíselné ID, které ho jedinečně identifikuje v doméně aplikace a je možné k němu přistupovat pomocí vlastnosti Task.Id. ID je užitečné pro zobrazení informací o úkolu v ladicím programu sady Visual Studio v okně Paralelní zásobníky a v okně Úkoly. ID je vytvořeno líným způsobem, což znamená, že není vytvořeno, dokud není vyžádáno. Proto může mít úkol při každém spuštění programu jiné ID. Další informace o zobrazení ID úloh v ladicím programu naleznete v tématu Použití okna Úlohy a Použití okna Paralelní zásobníky.
Možnosti vytváření úkolů
Většina rozhraní API, která vytvářejí úlohy, poskytují přetížení, která přijímají parametr TaskCreationOptions. Zadáním jedné nebo více těchto možností sdělíte plánovači úloh, jak naplánovat úlohu ve fondu vláken. Možnosti se můžou zkombinovat pomocí bitové operace NEBO.
Následující příklad ukazuje úlohu s možnostmi LongRunning a PreferFairness:
var task3 = new Task(() => MyLongRunningMethod(),
TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
task3.Start();
Dim task3 = New Task(Sub() MyLongRunningMethod(),
TaskCreationOptions.LongRunning Or TaskCreationOptions.PreferFairness)
task3.Start()
Úlohy, vlákna a kulturní nastavení
Každé vlákno má přidruženou kulturu a kulturu uživatelského rozhraní, které jsou definovány vlastnostmi Thread.CurrentCulture a Thread.CurrentUICulture. Kulturní nastavení vlákna se používá v operacích, jako je formátování, parsování, řazení a operace porovnání řetězců. Kultura uživatelského rozhraní vlákna se používá pro vyhledávání prostředků.
Systémová jazyková verze definuje výchozí jazykovou verzi a jazykovou verzi uživatelského rozhraní vlákna. Pomocí vlastností CultureInfo.DefaultThreadCurrentCulture a CultureInfo.DefaultThreadCurrentUICulture však můžete zadat výchozí jazykovou verzi pro všechna vlákna v doméně aplikace. Pokud explicitně nastavíte jazykovou verzi vlákna a spustíte nové vlákno, nové vlákno nezdědí jazykovou verzi volajícího vlákna; místo toho je jeho jazyková verze výchozí systémovou jazykovou verzí. V programování založeném na úlohách však úlohy používají kulturu volajícího vlákna, i když úloha běží asynchronně na jiném vlákně.
Následující příklad obsahuje jednoduchý obrázek. Změní aktuální jazykové nastavení aplikace na francouzštinu (Francie). Pokud je již aktuální kulturou francouzština (Francie), změní se na angličtinu (Spojené státy). Potom vyvolá delegáta s názvem formatDelegate
, který vrátí některá čísla formátovaná jako měnové hodnoty v novém kulturním prostředí. Bez ohledu na to, zda je delegát vyvolán úkolem synchronně nebo asynchronně, úloha používá kulturu volajícího vlákna.
using System;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
public class Example
{
public static void Main()
{
decimal[] values = { 163025412.32m, 18905365.59m };
string formatString = "C2";
Func<String> formatDelegate = () => { string output = String.Format("Formatting using the {0} culture on thread {1}.\n",
CultureInfo.CurrentCulture.Name,
Thread.CurrentThread.ManagedThreadId);
foreach (var value in values)
output += String.Format("{0} ", value.ToString(formatString));
output += Environment.NewLine;
return output;
};
Console.WriteLine($"The example is running on thread {Thread.CurrentThread.ManagedThreadId}");
// Make the current culture different from the system culture.
Console.WriteLine($"The current culture is {CultureInfo.CurrentCulture.Name}");
if (CultureInfo.CurrentCulture.Name == "fr-FR")
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
else
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");
Console.WriteLine($"Changed the current culture to {CultureInfo.CurrentCulture.Name}.\n");
// Execute the delegate synchronously.
Console.WriteLine("Executing the delegate synchronously:");
Console.WriteLine(formatDelegate());
// Call an async delegate to format the values using one format string.
Console.WriteLine("Executing a task asynchronously:");
var t1 = Task.Run(formatDelegate);
Console.WriteLine(t1.Result);
Console.WriteLine("Executing a task synchronously:");
var t2 = new Task<String>(formatDelegate);
t2.RunSynchronously();
Console.WriteLine(t2.Result);
}
}
// The example displays the following output:
// The example is running on thread 1
// The current culture is en-US
// Changed the current culture to fr-FR.
//
// Executing the delegate synchronously:
// Formatting using the fr-FR culture on thread 1.
// 163 025 412,32 € 18 905 365,59 €
//
// Executing a task asynchronously:
// Formatting using the fr-FR culture on thread 3.
// 163 025 412,32 € 18 905 365,59 €
//
// Executing a task synchronously:
// Formatting using the fr-FR culture on thread 1.
// 163 025 412,32 € 18 905 365,59 €
Imports System.Globalization
Imports System.Threading
Module Example
Public Sub Main()
Dim values() As Decimal = {163025412.32D, 18905365.59D}
Dim formatString As String = "C2"
Dim formatDelegate As Func(Of String) = Function()
Dim output As String = String.Format("Formatting using the {0} culture on thread {1}.",
CultureInfo.CurrentCulture.Name,
Thread.CurrentThread.ManagedThreadId)
output += Environment.NewLine
For Each value In values
output += String.Format("{0} ", value.ToString(formatString))
Next
output += Environment.NewLine
Return output
End Function
Console.WriteLine("The example is running on thread {0}",
Thread.CurrentThread.ManagedThreadId)
' Make the current culture different from the system culture.
Console.WriteLine("The current culture is {0}",
CultureInfo.CurrentCulture.Name)
If CultureInfo.CurrentCulture.Name = "fr-FR" Then
Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
Else
Thread.CurrentThread.CurrentCulture = New CultureInfo("fr-FR")
End If
Console.WriteLine("Changed the current culture to {0}.",
CultureInfo.CurrentCulture.Name)
Console.WriteLine()
' Execute the delegate synchronously.
Console.WriteLine("Executing the delegate synchronously:")
Console.WriteLine(formatDelegate())
' Call an async delegate to format the values using one format string.
Console.WriteLine("Executing a task asynchronously:")
Dim t1 = Task.Run(formatDelegate)
Console.WriteLine(t1.Result)
Console.WriteLine("Executing a task synchronously:")
Dim t2 = New Task(Of String)(formatDelegate)
t2.RunSynchronously()
Console.WriteLine(t2.Result)
End Sub
End Module
' The example displays the following output:
'
' The example is running on thread 1
' The current culture is en-US
' Changed the current culture to fr-FR.
'
' Executing the delegate synchronously:
' Formatting Imports the fr-FR culture on thread 1.
' 163 025 412,32 € 18 905 365,59 €
'
' Executing a task asynchronously:
' Formatting Imports the fr-FR culture on thread 3.
' 163 025 412,32 € 18 905 365,59 €
'
' Executing a task synchronously:
' Formatting Imports the fr-FR culture on thread 1.
' 163 025 412,32 € 18 905 365,59 €
Poznámka
Ve verzích rozhraní .NET Framework starších než .NET Framework 4.6 je jazyková verze úlohy určena jazykovou verzí vlákna, na kterém běží, nikoli jazykovou verzí volajícího vlákna. U asynchronních úloh se kultura používaná úlohou může lišit od kultury volajícího vlákna.
Další informace o asynchronních úlohách a kultuře najdete v části Kultura a asynchronní operace založené na úlohách v článku CultureInfo.
Vytváření návazností úkolů
Metody Task.ContinueWith a Task<TResult>.ContinueWith umožňují určit úkol, který se má spustit, když předcházející úkol skončí. Delegát úkolu pokračování je předán odkaz na úkol antecedentní, aby mohl prozkoumat stav úkolu antecedentního. A načtením hodnoty vlastnosti Task<TResult>.Result můžete jako vstup pro navazující proces použít výstup předchůdce.
V následujícím příkladu je úloha getData
spuštěna voláním TaskFactory.StartNew<TResult>(Func<TResult>) metody. Úloha processData
se spustí automaticky po dokončení getData
a displayData
se spustí po dokončení processData
.
getData
vytvoří celočíselné pole, které je přístupné pro processData
úkol prostřednictvím vlastnosti getData
úkolu Task<TResult>.Result.
processData
úlohy zpracuje toto pole a vrátí výsledek, jehož typ je odvozen z návratového typu výrazu lambda předaného metodě Task<TResult>.ContinueWith<TNewResult>(Func<Task<TResult>,TNewResult>). Úloha displayData
se spustí automaticky po dokončení processData
a objekt Tuple<T1,T2,T3> vrácený lambda výrazem processData
je přístupný úloze displayData
prostřednictvím vlastnosti processData
úlohy Task<TResult>.Result. Úkol displayData
převezme výsledek processData
úkolu. Vytvoří výsledek, jehož typ je odvozen podobným způsobem a který je zpřístupněn programu v Result vlastnosti.
using System;
using System.Threading.Tasks;
public class ContinuationOne
{
public static void Main()
{
var getData = Task.Factory.StartNew(() => {
Random rnd = new Random();
int[] values = new int[100];
for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
values[ctr] = rnd.Next();
return values;
} );
var processData = getData.ContinueWith((x) => {
int n = x.Result.Length;
long sum = 0;
double mean;
for (int ctr = 0; ctr <= x.Result.GetUpperBound(0); ctr++)
sum += x.Result[ctr];
mean = sum / (double) n;
return Tuple.Create(n, sum, mean);
} );
var displayData = processData.ContinueWith((x) => {
return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
x.Result.Item1, x.Result.Item2,
x.Result.Item3);
} );
Console.WriteLine(displayData.Result);
}
}
// The example displays output similar to the following:
// N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
Namespace ContinuationsOne
Module Example
Public Sub Main()
Dim getData = Task.Factory.StartNew(Function()
Dim rnd As New Random()
Dim values(99) As Integer
For ctr = 0 To values.GetUpperBound(0)
values(ctr) = rnd.Next()
Next
Return values
End Function)
Dim processData = getData.ContinueWith(Function(x)
Dim n As Integer = x.Result.Length
Dim sum As Long
Dim mean As Double
For ctr = 0 To x.Result.GetUpperBound(0)
sum += x.Result(ctr)
Next
mean = sum / n
Return Tuple.Create(n, sum, mean)
End Function)
Dim displayData = processData.ContinueWith(Function(x)
Return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
x.Result.Item1, x.Result.Item2,
x.Result.Item3)
End Function)
Console.WriteLine(displayData.Result)
End Sub
End Module
' The example displays output like the following:
' N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
End Namespace
Protože Task.ContinueWith je metoda instance, můžete zřetězit volání metod, namísto vytvoření objektu Task<TResult> pro každou předchozí úlohu. Následující příklad je funkčně identický s předchozím, s tím rozdílem, že zřetězí dohromady volání Task.ContinueWith metody. Objekt Task<TResult> vrácený řetězem volání metody je posledním pokračováním úkolu.
using System;
using System.Threading.Tasks;
public class ContinuationTwo
{
public static void Main()
{
var displayData = Task.Factory.StartNew(() => {
Random rnd = new Random();
int[] values = new int[100];
for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
values[ctr] = rnd.Next();
return values;
} ).
ContinueWith((x) => {
int n = x.Result.Length;
long sum = 0;
double mean;
for (int ctr = 0; ctr <= x.Result.GetUpperBound(0); ctr++)
sum += x.Result[ctr];
mean = sum / (double) n;
return Tuple.Create(n, sum, mean);
} ).
ContinueWith((x) => {
return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
x.Result.Item1, x.Result.Item2,
x.Result.Item3);
} );
Console.WriteLine(displayData.Result);
}
}
// The example displays output similar to the following:
// N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
Namespace ContinuationsTwo
Module Example
Public Sub Main()
Dim displayData = Task.Factory.StartNew(Function()
Dim rnd As New Random()
Dim values(99) As Integer
For ctr = 0 To values.GetUpperBound(0)
values(ctr) = rnd.Next()
Next
Return values
End Function). _
ContinueWith(Function(x)
Dim n As Integer = x.Result.Length
Dim sum As Long
Dim mean As Double
For ctr = 0 To x.Result.GetUpperBound(0)
sum += x.Result(ctr)
Next
mean = sum / n
Return Tuple.Create(n, sum, mean)
End Function). _
ContinueWith(Function(x)
Return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
x.Result.Item1, x.Result.Item2,
x.Result.Item3)
End Function)
Console.WriteLine(displayData.Result)
End Sub
End Module
' The example displays output like the following:
' N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
End Namespace
Metody ContinueWhenAll a ContinueWhenAny umožňují pokračovat ve více úkolech.
Pro další informace, viz téma řetězení úkolů pomocí pokračovacích úkolů.
Vytváření nezávislých podřízených úkolů
Když uživatelský kód spuštěný v úloze vytvoří nový úkol a nezadá AttachedToParent možnost, nebude nový úkol synchronizován s nadřazeným úkolem žádným zvláštním způsobem. Tento typ nesynchronní úlohy se nazývá odpojený vnořený úkol nebo odpojený podřízený úkol. Následující příklad ukazuje úlohu, která vytvoří jednu odpojenou podřízenou úlohu:
var outer = Task.Factory.StartNew(() =>
{
Console.WriteLine("Outer task beginning.");
var child = Task.Factory.StartNew(() =>
{
Thread.SpinWait(5000000);
Console.WriteLine("Detached task completed.");
});
});
outer.Wait();
Console.WriteLine("Outer task completed.");
// The example displays the following output:
// Outer task beginning.
// Outer task completed.
// Detached task completed.
Dim outer = Task.Factory.StartNew(Sub()
Console.WriteLine("Outer task beginning.")
Dim child = Task.Factory.StartNew(Sub()
Thread.SpinWait(5000000)
Console.WriteLine("Detached task completed.")
End Sub)
End Sub)
outer.Wait()
Console.WriteLine("Outer task completed.")
' The example displays the following output:
' Outer task beginning.
' Outer task completed.
' Detached child completed.
Poznámka
Nadřazený úkol nečeká na dokončení odděleného podřízeného úkolu.
Vytváření dílčích úkolů
Když uživatelský kód spuštěný v úkolu vytvoří úkol s možností AttachedToParent, nový úkol je znám jako připojený podřízený úkol k nadřazenému úkolu. Možnost AttachedToParent můžete použít k vyjádření paralelismu strukturovaných úkolů, protože nadřazená úloha implicitně čeká na dokončení všech připojených podřízených úkolů. Následující příklad ukazuje nadřazený úkol, který vytvoří 10 připojených podřízených úkolů. Příklad volá metodu Task.Wait, která čeká na dokončení nadřazené úlohy. Není nutné explicitně čekat na dokončení připojených podřízených úkolů.
using System;
using System.Threading;
using System.Threading.Tasks;
public class Child
{
public static void Main()
{
var parent = Task.Factory.StartNew(() => {
Console.WriteLine("Parent task beginning.");
for (int ctr = 0; ctr < 10; ctr++) {
int taskNo = ctr;
Task.Factory.StartNew((x) => {
Thread.SpinWait(5000000);
Console.WriteLine($"Attached child #{x} completed.");
},
taskNo, TaskCreationOptions.AttachedToParent);
}
});
parent.Wait();
Console.WriteLine("Parent task completed.");
}
}
// The example displays output like the following:
// Parent task beginning.
// Attached child #9 completed.
// Attached child #0 completed.
// Attached child #8 completed.
// Attached child #1 completed.
// Attached child #7 completed.
// Attached child #2 completed.
// Attached child #6 completed.
// Attached child #3 completed.
// Attached child #5 completed.
// Attached child #4 completed.
// Parent task completed.
Imports System.Threading
Namespace Child
Module Example
Public Sub Main()
Dim parent = Task.Factory.StartNew(Sub()
Console.WriteLine("Parent task beginning.")
For ctr As Integer = 0 To 9
Dim taskNo As Integer = ctr
Task.Factory.StartNew(Sub(x)
Thread.SpinWait(5000000)
Console.WriteLine("Attached child #{0} completed.",
x)
End Sub,
taskNo, TaskCreationOptions.AttachedToParent)
Next
End Sub)
parent.Wait()
Console.WriteLine("Parent task completed.")
End Sub
End Module
' The example displays output like the following:
' Parent task beginning.
' Attached child #9 completed.
' Attached child #0 completed.
' Attached child #8 completed.
' Attached child #1 completed.
' Attached child #7 completed.
' Attached child #2 completed.
' Attached child #6 completed.
' Attached child #3 completed.
' Attached child #5 completed.
' Attached child #4 completed.
' Parent task completed.
End Namespace
Nadřazený úkol může použít možnost TaskCreationOptions.DenyChildAttach, aby se ostatní úkoly nemohly připojit k nadřazené úloze. Další informace naleznete v tématu Připojené a odpojené podřízené úkoly.
Čekání na dokončení úkolů
Typy System.Threading.Tasks.Task a System.Threading.Tasks.Task<TResult> poskytují několik přetížení metod Task.Wait, které vám umožňují počkat na dokončení úkolu. Navíc přetížení statických metod Task.WaitAll a Task.WaitAny umožňuje vyčkat na dokončení libovolného úkolu nebo všech úkolů z pole.
Obvykle byste čekali na úlohu z jednoho z těchto důvodů:
Hlavní vlákno závisí na konečném výsledku vypočítaného úlohou.
Musíte zpracovat výjimky, které mohou být vyvolány úkolem.
Aplikace se může ukončit před dokončením provádění všech úkolů. Například konzolové aplikace se ukončí po provedení veškerého synchronního kódu v
Main
(vstupní bod aplikace).
Následující příklad ukazuje základní vzor, který nezahrnuje zpracování výjimek:
Task[] tasks = new Task[3]
{
Task.Factory.StartNew(() => MethodA()),
Task.Factory.StartNew(() => MethodB()),
Task.Factory.StartNew(() => MethodC())
};
//Block until all tasks complete.
Task.WaitAll(tasks);
// Continue on this thread...
Dim tasks() =
{
Task.Factory.StartNew(Sub() MethodA()),
Task.Factory.StartNew(Sub() MethodB()),
Task.Factory.StartNew(Sub() MethodC())
}
' Block until all tasks complete.
Task.WaitAll(tasks)
' Continue on this thread...
Příklad, který ukazuje zpracování výjimek, viz Zpracování výjimek.
Některá přetížení vám umožňují určit časový limit, zatímco jiná přijímají jako vstupní parametr další CancellationToken, aby samotné čekání bylo možné zrušit buď programově, nebo v reakci na uživatelský vstup.
Když čekáte na úkol, implicitně čekáte na všechny podúkoly tohoto úkolu vytvořené pomocí možnosti TaskCreationOptions.AttachedToParent. Task.Wait se vrátí okamžitě, pokud už je úkol dokončený. Metoda Task.Wait vyvolá všechny výjimky vyvolané úlohou, i když byla Task.Wait metoda volána po dokončení úkolu.
Sestavování úkolů
Třídy Task a Task<TResult> poskytují několik metod, které vám pomůžou vytvářet více úkolů. Tyto metody implementují běžné vzory a využívají lépe asynchronní jazykové funkce poskytované jazykem C#, Visual Basic a F#. Tato část popisuje metody WhenAll, WhenAny, Delaya FromResult.
Task.WhenAll (metoda zajišťující simultánní provedení více úkolů)
Metoda Task.WhenAll asynchronně čeká na dokončení více Task nebo Task<TResult> objektů. Poskytuje přetížené verze, které umožňují čekat na nejednotné sady úloh. Můžete například počkat na dokončení více Task a Task<TResult> objektů z jednoho volání metody.
Task.WhenAny
Metoda Task.WhenAny asynchronně čeká na dokončení jednoho z více Task nebo Task<TResult> objektů. Stejně jako v metodě Task.WhenAll poskytuje tato metoda přetížené verze, které umožňují čekat na neuniformní sady úloh. Metoda WhenAny je zvlášť užitečná v následujících scénářích:
redundantní operace: Zvažte algoritmus nebo operaci, kterou lze provádět mnoha způsoby. Metodu WhenAny můžete použít k výběru operace, která se dokončí jako první, a zrušení zbývajících operací.
prokládané operace: Můžete spustit více operací, které musí být dokončeny, a použít metodu WhenAny ke zpracování výsledků, jakmile každá operace skončí. Po dokončení jedné operace můžete spustit jeden nebo více úkolů.
Škrcené operace: Pomocí metody WhenAny můžete vylepšit předchozí scénář omezením počtu souběžných operací.
operace s vypršenou platností: Pomocí metody WhenAny můžete vybrat jeden nebo více úkolů a úkol, který se dokončí po určité době, například úkol vrácený metodou Delay. Metoda Delay je popsaná v následující části.
Task.Delay
Metoda Task.Delay vytvoří objekt Task, který se dokončí po zadaném čase. Tuto metodu můžete použít k sestavení smyček, které se dotazují na data, k určení časových limitů, ke zpoždění zpracování vstupu uživatele atd.
Task(T). FromResult
Pomocí metody Task.FromResult můžete vytvořit objekt Task<TResult>, který obsahuje předem vypočítaný výsledek. Tato metoda je užitečná při provádění asynchronní operace, která vrací objekt Task<TResult> a výsledek tohoto Task<TResult> objektu je již vypočítán. Příklad, který používá FromResult k načtení výsledků asynchronních operací stahování uložených v mezipaměti, najdete v tématu Postupy: Vytváření předem vypočítaných úloh.
Zpracování výjimek v úlohách
Když úloha vyvolá jednu nebo více výjimek, jsou výjimky zabaleny do AggregateException výjimky. Tato výjimka je propagována zpět do vlákna, které se spojí s úlohou. Obvykle se jedná o vlákno, které čeká na dokončení úlohy, nebo vlákno, které přistupuje k vlastnosti Result. Toto chování prosazuje zásadu rozhraní .NET Framework, že všechny neošetřené výjimky by ve výchozím nastavení měly ukončit proces. Volající kód může zpracovávat výjimky pomocí některého z následujících způsobů v bloku try
/catch
:
Spojovací vlákno může také zpracovávat výjimky tím, že přistupuje k vlastnosti Exception dříve, než je úloha smetena garbage collectorem. Přístupem k této vlastnosti zabráníte neošetřené výjimce ve spuštění mechanismu šíření výjimky, který ukončí proces při finalizaci objektu.
Další informace o výjimkách a úlohách naleznete v tématu Zpracování výjimek.
Zrušení úkolů
Třída Task podporuje kooperativní zrušení a je plně integrovaná s třídami System.Threading.CancellationTokenSource a System.Threading.CancellationToken, které byly zavedeny v rozhraní .NET Framework 4. Mnoho konstruktorů ve třídě System.Threading.Tasks.Task vezme CancellationToken objekt jako vstupní parametr. Mnoho přetížení StartNew a Run také obsahuje parametr CancellationToken.
Token můžete vytvořit a později vydat žádost o zrušení pomocí třídy CancellationTokenSource. Předejte token Task jako argument a odkazujte také na stejný token ve vašem delegátu uživatele, který funguje při odpovídání na žádost o zrušení.
Další informace naleznete v tématu Zrušení úlohy a Jak na to: Zrušení úlohy a jejích podřízených.
třída TaskFactory
Třída TaskFactory poskytuje statické metody, které zapouzdřují běžné vzory pro vytváření a spouštění úloh a pokračování úkolů.
Nejběžnější vzor je StartNew, který vytvoří a spustí úlohu v jednom příkazu.
Když vytváříte úlohy pokračování z více objektů typu antecedent, použijte metodu ContinueWhenAll nebo metodu ContinueWhenAny nebo jejich ekvivalenty ve třídě Task<TResult>. Pro další informace, viz téma řetězení úkolů pomocí pokračovacích úkolů.
Pro zapouzdření metod asynchronního programovacího modelu
BeginX
aEndX
v instanci Task nebo Task<TResult> použijte metody FromAsync. Další informace naleznete v tématu TPL a Tradiční asynchronní programování rozhraní .NET Framework.
Výchozí TaskFactory lze přistupovat jako ke statické vlastnosti třídy Task nebo Task<TResult>. Můžete také vytvořit instanci TaskFactory přímo a zadat různé možnosti, které zahrnují CancellationToken, možnost TaskCreationOptions, TaskContinuationOptions nebo TaskScheduler. Jakékoli možnosti, které se zadají při vytváření objektu pro vytváření úloh, se použijí pro všechny úlohy, které vytvoří, pokud Task není vytvořen pomocí výčtu TaskCreationOptions, v takovém případě možnosti úkolu přepíšou možnosti objektu pro vytváření úloh.
Úkoly bez delegátů
V některých případech můžete chtít použít Task k zapouzdření některých asynchronních operací, které provádí externí komponenta místo delegáta uživatele. Pokud je operace založená na vzoru Begin/End asynchronního programovacího modelu, můžete použít FromAsync metody. Pokud tomu tak není, můžete pomocí objektu TaskCompletionSource<TResult> zabalit operaci do úlohy a získat tak některé výhody Task programovatelnosti. Například podpora šíření výjimek a pokračování. Další informace najdete v tématu TaskCompletionSource<TResult>.
Vlastní plánovače
Většina vývojářů aplikací nebo knihoven se nezajímá, na kterém procesoru běží úloha, jak synchronizuje svou práci s jinými úlohami nebo jak je naplánovaná na System.Threading.ThreadPool. Vyžadují, aby se na hostitelském počítači spustil co nejefektivněji. Pokud potřebujete jemněji odstupňovanou kontrolu nad podrobnostmi plánování, TPL vám umožní nakonfigurovat některá nastavení výchozího plánovače úloh a dokonce vám umožní zadat vlastní plánovač. Další informace najdete v tématu TaskScheduler.
Související datové struktury
TPL má několik nových veřejných typů, které jsou užitečné v paralelních a sekvenčních scénářích. Patří mezi ně několik tříd kolekcí, které jsou bezpečné pro vlákna, rychlé a škálovatelné, v oboru názvů System.Collections.Concurrent a několik nových typů synchronizace. Například System.Threading.Semaphore a System.Threading.ManualResetEventSlim, které jsou efektivnější než jejich předchůdci pro konkrétní druhy úloh. Jiné nové typy v rozhraní .NET Framework 4, například System.Threading.Barrier a System.Threading.SpinLock, poskytují funkce, které nebyly k dispozici v předchozích verzích. Další informace naleznete v tématu datové struktury pro paralelní programování.
Vlastní typy úkolů
Doporučujeme, abyste nedědili z System.Threading.Tasks.Task ani System.Threading.Tasks.Task<TResult>. Místo toho doporučujeme použít vlastnost AsyncState k přidružení dalších dat nebo stavu k objektu Task nebo Task<TResult>. Pomocí rozšiřujících metod můžete také rozšířit funkce Task a tříd Task<TResult>. Další informace o metodách rozšíření naleznete v tématu Metody rozšíření a Metody rozšíření.
Pokud je nutné dědit z Task nebo Task<TResult>, nemůžete použít třídy Run nebo System.Threading.Tasks.TaskFactory, System.Threading.Tasks.TaskFactory<TResult>nebo System.Threading.Tasks.TaskCompletionSource<TResult> k vytvoření instancí vlastního typu úlohy. Nemůžete je použít, protože tyto třídy vytvářejí pouze Task a Task<TResult> objekty. Kromě toho nemůžete použít mechanismy pokračování úkolů, které poskytuje Task, Task<TResult>, TaskFactorya TaskFactory<TResult> k vytvoření instancí vlastního typu úlohy. Nemůžete je použít, protože tyto třídy také vytvářejí pouze Task a Task<TResult> objekty.
Související oddíly
Titul | Popis |
---|---|
řetězení úkolů pomocí pokračujících úkolů | Popisuje, jak fungují pokračování. |
Připojené a odpojené podřízené úkoly | Popisuje rozdíl mezi připojenými a odpojenými podřízenými úlohami. |
zrušení úlohy | Popisuje podporu zrušení, která je integrovaná do objektu Task. |
zpracování výjimek | Popisuje, jak se zpracovávají výjimky v souběžných vláknech. |
Postupy: Použití Parallel.Invoke ke spouštění paralelních operací | Popisuje, jak používat Invoke. |
Jak vrátit hodnotu z úkolu | Popisuje, jak vrátit hodnoty z úkolů. |
Postupy: Zrušení úlohy a jejích podřízených úloh | Popisuje, jak zrušit úkoly. |
Postupy: Vytváření předem vypočítaných úloh | Popisuje, jak pomocí metody Task.FromResult načíst výsledky asynchronních operací stahování uložených v mezipaměti. |
Postupy: Procházení binárního stromu pomocí paralelních úloh | Popisuje, jak pomocí úkolů procházet binární strom. |
Jak na to: Rozbalení vnořené úlohy | Ukazuje, jak používat rozšiřující metodu Unwrap. |
Paralelismus dat | Popisuje, jak používat For a ForEach k vytváření paralelních smyček nad daty. |
paralelní programování | Uzel nejvyšší úrovně pro paralelní programování rozhraní .NET Framework |