Programação assíncrona baseada em tarefas
A TPL (Task Parallel Library) baseia-se no conceito de uma tarefa, que representa uma operação assíncrona. De certa forma, uma tarefa se assemelha a um thread ou ThreadPool item de trabalho, mas em um nível mais alto de abstração. O termo paralelismo de tarefas refere-se a uma ou mais tarefas independentes executadas simultaneamente. As tarefas oferecem dois benefícios principais:
Utilização mais eficiente e escalável dos recursos do sistema.
Nos bastidores, as tarefas são enfileiradas para o ThreadPool, que foi aprimorado com algoritmos que determinam e se ajustam ao número de threads. Esses algoritmos fornecem balanceamento de carga para maximizar a taxa de transferência. Esse processo torna as tarefas relativamente leves, e você pode criar muitas delas para permitir um paralelismo refinado.
Mais controle programático do que é possível com um thread ou item de trabalho.
As tarefas e a estrutura construída em torno delas fornecem um rico conjunto de APIs que suportam espera, cancelamento, continuações, tratamento robusto de exceções, status detalhado, agendamento personalizado e muito mais.
Por ambos os motivos, a TPL é a API preferida para escrever código multi-threaded, assíncrono e paralelo no .NET.
Criação e execução de tarefas implicitamente
O Parallel.Invoke método fornece uma maneira conveniente de executar qualquer número de instruções arbitrárias simultaneamente. Basta passar um Action delegado para cada item de trabalho. A maneira mais fácil de criar esses delegados é usar expressões lambda. A expressão lambda pode chamar um método nomeado ou fornecer o código embutido. O exemplo a seguir mostra uma chamada básica Invoke que cria e inicia duas tarefas que são executadas simultaneamente. A primeira tarefa é representada por uma expressão lambda que chama um método chamado DoSomeWork
, e a segunda tarefa é representada por uma expressão lambda que chama um método chamado DoSomeOtherWork
.
Nota
Esta documentação usa expressões lambda para definir delegados na TPL. Se você não estiver familiarizado com expressões lambda em C# ou Visual Basic, consulte Expressões lambda em PLINQ e TPL.
Parallel.Invoke(() => DoSomeWork(), () => DoSomeOtherWork());
Parallel.Invoke(Sub() DoSomeWork(), Sub() DoSomeOtherWork())
Nota
O número de Task instâncias criadas nos Invoke bastidores não é necessariamente igual ao número de delegados fornecidos. O TPL pode empregar várias otimizações, especialmente com um grande número de delegados.
Para obter mais informações, consulte Como usar Parallel.Invoke para executar operações paralelas.
Para obter maior controle sobre a execução da tarefa ou para retornar um valor da tarefa, você deve trabalhar com Task objetos de forma mais explícita.
Criando e executando tarefas explicitamente
Uma tarefa que não retorna um valor é representada pela System.Threading.Tasks.Task classe. Uma tarefa que retorna um valor é representada pela System.Threading.Tasks.Task<TResult> classe, que herda de Task. O objeto de tarefa lida com os detalhes da infraestrutura e fornece métodos e propriedades que são acessíveis a partir do thread de chamada durante todo o tempo de vida da tarefa. Por exemplo, você pode acessar a Status propriedade de uma tarefa a qualquer momento para determinar se ela começou a ser executada, executada até a conclusão, foi cancelada ou lançou uma exceção. O status é representado por uma TaskStatus enumeração.
Ao criar uma tarefa, você dá a ela um delegado de usuário que encapsula o código que a tarefa executará. O delegado pode ser expresso como um delegado nomeado, um método anônimo ou uma expressão lambda. As expressões do Lambda podem conter uma chamada para um método nomeado, conforme mostrado no exemplo a seguir. O exemplo inclui uma chamada para o Task.Wait método para garantir que a tarefa conclua a execução antes que o aplicativo de modo de console termine.
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 '{0}'.",
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
Você também pode usar os Task.Run métodos para criar e iniciar uma tarefa em uma operação. Para gerenciar a tarefa, os Run métodos usam o agendador de tarefas padrão, independentemente de qual agendador de tarefas está associado ao thread atual. Os Run métodos são a maneira preferida de criar e iniciar tarefas quando não é necessário mais controle sobre a criação e o agendamento da tarefa.
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 '{0}'.",
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
Você também pode usar o TaskFactory.StartNew método para criar e iniciar uma tarefa em uma operação. Como mostrado no exemplo a seguir, você pode usar esse método quando:
A criação e o agendamento não precisam ser separados e você precisa de opções adicionais de criação de tarefas ou o uso de um agendador específico.
Você precisa passar um estado adicional para a tarefa que você pode recuperar através de sua Task.AsyncState propriedade.
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 e Task<TResult> cada um expõe uma propriedade estática Factory que retorna uma instância padrão de , para TaskFactoryque você possa chamar o método como Task.Factory.StartNew()
. Além disso, no exemplo a seguir, como as tarefas são do tipo System.Threading.Tasks.Task<TResult>, cada uma delas tem uma propriedade pública Task<TResult>.Result que contém o resultado da computação. As tarefas são executadas de forma assíncrona e podem ser concluídas em qualquer ordem. Se a propriedade for acessada Result antes da conclusão do cálculo, a propriedade bloqueará o thread de chamada até que o valor esteja disponível.
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("{0:N1}", sum);
}
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
Para obter mais informações, consulte Como retornar um valor de uma tarefa.
Ao usar uma expressão lambda para criar um delegado, você tem acesso a todas as variáveis visíveis nesse ponto do código-fonte. No entanto, em alguns casos, principalmente dentro de loops, um lambda não captura a variável como esperado. Ele captura apenas a referência da variável, não o valor, pois ele muda após cada iteração. O exemplo a seguir ilustra o problema. Ele passa um contador de loop para uma expressão lambda que instancia um CustomData
objeto e usa o contador de loop como identificador do objeto. Como mostra a saída do exemplo, cada CustomData
objeto tem um identificador idêntico.
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 #{0} created at {1} on thread #{2}.",
data.Name, data.CreationTime, 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
Você pode acessar o valor em cada iteração fornecendo um objeto de estado para uma tarefa por meio de seu construtor. O exemplo a seguir modifica o exemplo anterior usando o contador de loop ao criar o CustomData
objeto, que, por sua vez, é passado para a expressão lambda. Como mostra a saída do exemplo, cada CustomData
objeto agora tem um identificador exclusivo baseado no valor do contador de loop no momento em que o objeto foi instanciado.
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 #{0} created at {1} on thread #{2}.",
data.Name, data.CreationTime, 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
Esse estado é passado como um argumento para o delegado de tarefa e pode ser acessado a partir do objeto de tarefa usando a Task.AsyncState propriedade. O exemplo a seguir é uma variação do exemplo anterior. Ele usa a AsyncState propriedade para exibir informações sobre os objetos passados CustomData
para a expressão 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 de tarefa
Cada tarefa recebe um ID inteiro que a identifica exclusivamente em um domínio de aplicativo e pode ser acessada usando a Task.Id propriedade. A ID é útil para exibir informações de tarefas nas janelas Pilhas paralelas e tarefas do depurador do Visual Studio. O ID é criado preguiçosamente, o que significa que não é criado até ser solicitado. Portanto, uma tarefa pode ter um ID diferente cada vez que o programa é executado. Para obter mais informações sobre como exibir IDs de tarefas no depurador, consulte Usando a janela Tarefas e Usando a janela Pilhas paralelas.
Opções de criação de tarefas
A maioria das APIs que criam tarefas fornece sobrecargas que aceitam um TaskCreationOptions parâmetro. Ao especificar uma ou mais dessas opções, você informa ao agendador de tarefas como agendar a tarefa no pool de threads. As opções podem ser combinadas usando uma operação OR bit a bit.
O exemplo a seguir mostra uma tarefa que tem as LongRunning opções e 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()
Tarefas, threads e cultura
Cada thread tem uma cultura associada e uma cultura da interface do usuário, que são definidas pelas Thread.CurrentCulture propriedades e Thread.CurrentUICulture , respectivamente. A cultura de um thread é usada em operações como, formatação, análise, classificação e operações de comparação de cadeia de caracteres. A cultura da interface do usuário de um thread é usada na pesquisa de recursos.
A cultura do sistema define a cultura padrão e a cultura da interface do usuário de um thread. No entanto, você pode especificar uma cultura padrão para todos os threads em um domínio de aplicativo usando as CultureInfo.DefaultThreadCurrentCulture propriedades e CultureInfo.DefaultThreadCurrentUICulture . Se você definir explicitamente a cultura de um thread e iniciar um novo thread, o novo thread não herdará a cultura do thread de chamada; em vez disso, sua cultura é a cultura padrão do sistema. No entanto, na programação baseada em tarefas, as tarefas usam a cultura do thread de chamada, mesmo que a tarefa seja executada de forma assíncrona em um thread diferente.
O exemplo a seguir fornece uma ilustração simples. Ele muda a cultura atual do aplicativo para francês (França). Se o francês (França) já é a cultura atual, ele muda para o inglês (Estados Unidos). Em seguida, ele invoca um delegado chamado formatDelegate
que retorna alguns números formatados como valores de moeda na nova cultura. Se o delegado é invocado por uma tarefa de forma síncrona ou assíncrona, a tarefa usa a cultura do thread de chamada.
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 {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")
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
else
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");
Console.WriteLine("Changed the current culture to {0}.\n",
CultureInfo.CurrentCulture.Name);
// 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 €
Nota
Em versões do .NET Framework anteriores ao .NET Framework 4.6, a cultura de uma tarefa é determinada pela cultura do thread no qual ela é executada, não pela cultura do thread de chamada. Para tarefas assíncronas, a cultura usada pela tarefa pode ser diferente da cultura do thread de chamada.
Para obter mais informações sobre tarefas assíncronas e cultura, consulte a seção "Cultura e operações baseadas em tarefas assíncronas" no CultureInfo artigo.
Criando continuações de tarefas
Os Task.ContinueWith métodos e Task<TResult>.ContinueWith permitem especificar uma tarefa a ser iniciada quando a tarefa antecedente terminar. O delegado da tarefa de continuação recebe uma referência à tarefa antecedente para que possa examinar o status da tarefa antecedente. E recuperando o valor da Task<TResult>.Result propriedade, você pode usar a saída do antecedente como entrada para a continuação.
No exemplo a seguir, a getData
tarefa é iniciada por uma chamada para o TaskFactory.StartNew<TResult>(Func<TResult>) método. A processData
tarefa é iniciada automaticamente quando getData
termina e displayData
é iniciada quando processData
termina. getData
Produz uma matriz inteira, que é acessível à processData
tarefa através getData
da propriedade da Task<TResult>.Result tarefa. A processData
tarefa processa essa matriz e retorna um resultado cujo tipo é inferido do tipo de retorno da expressão lambda passada para o Task<TResult>.ContinueWith<TNewResult>(Func<Task<TResult>,TNewResult>) método. A displayData
tarefa é executada automaticamente quando processData
termina, e o Tuple<T1,T2,T3>processData
objeto retornado pela expressão lambda é acessível à displayData
tarefa por meio processData
da propriedade da Task<TResult>.Result tarefa. A displayData
tarefa toma o resultado da processData
tarefa. Produz um resultado cujo tipo é inferido de forma semelhante, e que é disponibilizado ao programa na Result propriedade.
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
Como Task.ContinueWith é um método de instância, você pode encadear chamadas de método juntas em vez de instanciar um Task<TResult> objeto para cada tarefa antecedente. O exemplo a seguir é funcionalmente idêntico ao anterior, exceto que ele encadeia chamadas para o Task.ContinueWith método. O Task<TResult> objeto retornado pela cadeia de chamadas de método é a tarefa de continuação final.
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
Os ContinueWhenAll métodos e ContinueWhenAny permitem que você continue a partir de várias tarefas.
Para obter mais informações, consulte Encadeando tarefas usando tarefas de continuação.
Criando tarefas filhas separadas
Quando o código de usuário em execução em uma tarefa cria uma nova tarefa e não especifica a AttachedToParent opção, a nova tarefa não é sincronizada com a tarefa pai de nenhuma maneira especial. Esse tipo de tarefa não sincronizada é chamada de tarefa aninhada desanexada ou tarefa filho desanexada. O exemplo a seguir mostra uma tarefa que cria uma tarefa filho desanexada:
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.
Nota
A tarefa pai não espera que a tarefa filho desanexada seja concluída.
Criando tarefas filhas
Quando o código de usuário em execução em uma tarefa cria uma tarefa com a AttachedToParent opção, a nova tarefa é conhecida como uma tarefa filha anexada da tarefa pai. Você pode usar a opção para expressar paralelismo AttachedToParent de tarefa estruturada porque a tarefa pai aguarda implicitamente a conclusão de todas as tarefas filhas anexadas. O exemplo a seguir mostra uma tarefa pai que cria 10 tarefas filhas anexadas. O exemplo chama o Task.Wait método para aguardar a conclusão da tarefa pai. Ele não precisa esperar explicitamente que as tarefas filhas anexadas sejam concluídas.
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 #{0} completed.",
x);
},
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
Uma tarefa pai pode usar a TaskCreationOptions.DenyChildAttach opção para impedir que outras tarefas sejam anexadas à tarefa pai. Para obter mais informações, consulte Tarefas filhas anexadas e desanexadas.
Aguardando a conclusão das tarefas
Os System.Threading.Tasks.Task tipos e System.Threading.Tasks.Task<TResult> fornecem várias sobrecargas dos métodos que permitem aguardar a conclusão de Task.Wait uma tarefa. Além disso, as sobrecargas da estática Task.WaitAll e Task.WaitAny dos métodos permitem que você aguarde a conclusão de qualquer uma ou de todas as tarefas de uma série de tarefas.
Normalmente, você esperaria por uma tarefa por um destes motivos:
O thread principal depende do resultado final calculado por uma tarefa.
Você tem que lidar com exceções que podem ser lançadas da tarefa.
O aplicativo pode ser encerrado antes que todas as tarefas tenham concluído a execução. Por exemplo, os aplicativos de console serão encerrados depois que todo o código síncrono (
Main
o ponto de entrada do aplicativo) for executado.
O exemplo a seguir mostra o padrão básico que não envolve o tratamento de exceções:
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...
Para obter um exemplo que mostra o tratamento de exceções, consulte Tratamento de exceções.
Algumas sobrecargas permitem especificar um tempo limite, e outras tomam um adicional CancellationToken como um parâmetro de entrada para que a espera em si possa ser cancelada programaticamente ou em resposta à entrada do usuário.
Quando você espera por uma tarefa, você implicitamente espera por todos os filhos dessa tarefa que foram criados usando a TaskCreationOptions.AttachedToParent opção. Task.Wait retorna imediatamente se a tarefa já tiver sido concluída. Um Task.Wait método lançará quaisquer exceções geradas por uma tarefa, mesmo que o Task.Wait método tenha sido chamado após a conclusão da tarefa.
Composição de tarefas
As Task classes e Task<TResult> fornecem vários métodos para ajudá-lo a compor várias tarefas. Esses métodos implementam padrões comuns e fazem melhor uso dos recursos de linguagem assíncrona fornecidos pelo C#, Visual Basic e F#. Esta seção descreve os WhenAllmétodos , WhenAny, Delay, e FromResult .
Tarefa.QuandoTodos
O Task.WhenAll método aguarda de forma assíncrona a conclusão de vários Task ou Task<TResult> objetos. Ele fornece versões sobrecarregadas que permitem aguardar conjuntos não uniformes de tarefas. Por exemplo, você pode aguardar a conclusão de vários Task objetos a Task<TResult> partir de uma chamada de método.
Tarefa.QuandoQualquer
O Task.WhenAny método aguarda de forma assíncrona a conclusão de um dos vários Task ou Task<TResult> objetos. Como no Task.WhenAll método, esse método fornece versões sobrecarregadas que permitem aguardar conjuntos não uniformes de tarefas. O WhenAny método é especialmente útil nos seguintes cenários:
Operações redundantes: considere um algoritmo ou operação que pode ser executada de várias maneiras. Você pode usar o WhenAny método para selecionar a operação que termina primeiro e, em seguida, cancelar as operações restantes.
Operações intercaladas: Você pode iniciar várias operações que devem ser concluídas e usar o WhenAny método para processar resultados à medida que cada operação é concluída. Após a conclusão de uma operação, você pode iniciar uma ou mais tarefas.
Operações limitadas: Você pode usar o WhenAny método para estender o cenário anterior limitando o número de operações simultâneas.
Operações expiradas: você pode usar o WhenAny método para selecionar entre uma ou mais tarefas e uma tarefa que termina após um tempo específico, como uma tarefa retornada Delay pelo método. O Delay método é descrito na seção a seguir.
Task.Delay
O Task.Delay método produz um Task objeto que termina após o tempo especificado. Você pode usar esse método para criar loops que pesquisam dados, para especificar tempos limites, para atrasar a manipulação da entrada do usuário e assim por diante.
Tarefa(T). FromResult
Usando o Task.FromResult método, você pode criar um Task<TResult> objeto que contém um resultado pré-calculado. Esse método é útil quando você executa uma operação assíncrona que retorna um Task<TResult> objeto e o resultado desse Task<TResult> objeto já está calculado. Para obter um exemplo usado FromResult para recuperar os resultados de operações de download assíncronas mantidas em um cache, consulte Como criar tarefas pré-computadas.
Tratamento de exceções em tarefas
Quando uma tarefa lança uma ou mais exceções, as exceções são encapsuladas em uma AggregateException exceção. Essa exceção é propagada de volta para o thread que se une à tarefa. Normalmente, é o thread aguardando a conclusão da tarefa ou o thread acessando a Result propriedade. Esse comportamento impõe a política do .NET Framework de que todas as exceções não tratadas por padrão devem encerrar o processo. O código de chamada pode lidar com as exceções usando qualquer um dos seguintes em um try
/catch
bloco:
O thread de junção também pode lidar com exceções acessando a Exception propriedade antes que a tarefa seja coletada de lixo. Ao acessar essa propriedade, você impede que a exceção não tratada acione o comportamento de propagação de exceção que encerra o processo quando o objeto é finalizado.
Para obter mais informações sobre exceções e tarefas, consulte Tratamento de exceções.
Cancelar tarefas
A Task classe suporta cancelamento cooperativo e é totalmente integrada com as System.Threading.CancellationTokenSource classes and System.Threading.CancellationToken , que foram introduzidas no .NET Framework 4. Muitos dos construtores na System.Threading.Tasks.Task classe tomam um CancellationToken objeto como um parâmetro de entrada. Muitas das StartNew sobrecargas também Run incluem um CancellationToken parâmetro.
Você pode criar o token e emitir a solicitação de cancelamento em algum momento posterior, usando a CancellationTokenSource classe. Passe o token para o Task como um argumento e também faça referência ao mesmo token em seu delegado de usuário, que faz o trabalho de responder a uma solicitação de cancelamento.
Para obter mais informações, consulte Cancelamento de tarefas e Como cancelar uma tarefa e seus filhos.
A classe TaskFactory
A TaskFactory classe fornece métodos estáticos que encapsulam padrões comuns para criar e iniciar tarefas e tarefas de continuação.
O padrão mais comum é StartNew, que cria e inicia uma tarefa em uma instrução.
Ao criar tarefas de continuação a partir de vários antecedentes, use o ContinueWhenAll método ou ContinueWhenAny método ou seus equivalentes na Task<TResult> classe. Para obter mais informações, consulte Encadeando tarefas usando tarefas de continuação.
Para encapsular o modelo
BeginX
de programação assíncrona eEndX
os métodos em uma Task instância ou Task<TResult> , use os FromAsync métodos. Para obter mais informações, consulte TPL e programação assíncrona tradicional do .NET Framework.
O padrão TaskFactory pode ser acessado Task como uma propriedade estática na classe ou Task<TResult> classe. Você também pode instanciar um TaskFactory diretamente e especificar várias opções que incluem um CancellationToken, uma TaskCreationOptions opção, uma TaskContinuationOptions opção ou um TaskSchedulerarquivo . Quaisquer opções especificadas quando você criar a fábrica de tarefas serão aplicadas a todas as tarefas que ela criar, a menos que a Task seja criada usando a TaskCreationOptions enumeração, caso em que as opções da tarefa substituem as da fábrica de tarefas.
Tarefas sem delegados
Em alguns casos, talvez você queira usar um Task para encapsular alguma operação assíncrona executada por um componente externo em vez do seu representante de usuário. Se a operação for baseada no padrão Begin/End do modelo de programação assíncrona, você poderá usar os FromAsync métodos. Se esse não for o caso, você pode usar o TaskCompletionSource<TResult> objeto para envolver a operação em uma tarefa e, assim, obter alguns dos benefícios da Task programação. Por exemplo, suporte para propagação de exceções e continuações. Para obter mais informações, veja TaskCompletionSource<TResult>.
Programadores personalizados
A maioria dos desenvolvedores de aplicativos ou bibliotecas não se importa em qual processador a tarefa é executada, como ele sincroniza seu trabalho com outras tarefas ou como está agendado no System.Threading.ThreadPool. Eles só exigem que ele seja executado da forma mais eficiente possível no computador host. Se você precisar de um controle mais refinado sobre os detalhes de agendamento, o TPL permite que você defina algumas configurações no agendador de tarefas padrão e até mesmo forneça um agendador personalizado. Para obter mais informações, veja TaskScheduler.
Estruturas de dados relacionadas
O TPL tem vários novos tipos públicos que são úteis em cenários paralelos e sequenciais. Isso inclui várias classes de coleção thread-safe, rápidas e escaláveis no System.Collections.Concurrent namespace e vários novos tipos de sincronização. Por exemplo, System.Threading.Semaphore e System.Threading.ManualResetEventSlim, que são mais eficientes do que seus antecessores para tipos específicos de cargas de trabalho. Outros novos tipos no .NET Framework 4, por exemplo, System.Threading.Barrier e System.Threading.SpinLock, fornecem funcionalidades que não estavam disponíveis em versões anteriores. Para obter mais informações, consulte Estruturas de dados para programação paralela.
Tipos de tarefas personalizados
Recomendamos que você não herde de System.Threading.Tasks.Task ou System.Threading.Tasks.Task<TResult>. Em vez disso, recomendamos que você use a AsyncState propriedade para associar dados ou estados adicionais a um Task ou Task<TResult> objeto. Você também pode usar métodos de extensão para estender a funcionalidade das Task classes and Task<TResult> . Para obter mais informações sobre métodos de extensão, consulte Métodos de extensão e métodos de extensão.
Se você precisar herdar de Task ou Task<TResult>, não poderá usar Run ou as System.Threading.Tasks.TaskFactoryclasses , System.Threading.Tasks.TaskFactory<TResult>, ou System.Threading.Tasks.TaskCompletionSource<TResult> para criar instâncias do seu tipo de tarefa personalizada. Você não pode usá-los porque essas classes criam apenas Task e Task<TResult> objetos. Além disso, você não pode usar os mecanismos de continuação de tarefas fornecidos por Task, Task<TResult>, TaskFactorye TaskFactory<TResult> para criar instâncias do seu tipo de tarefa personalizado. Você não pode usá-los porque essas classes também criam apenas Task e Task<TResult> objetos.
Secções relacionadas
Title | Description |
---|---|
Encadeando tarefas usando tarefas de continuação | Descreve como as continuações funcionam. |
Tarefas Anexadas e Separadas da Criança | Descreve a diferença entre tarefas filhas anexadas e separadas. |
Cancelamento de Tarefas | Descreve o suporte de cancelamento que está incorporado no Task objeto. |
Tratamento de exceções | Descreve como as exceções em threads simultâneos são tratadas. |
Como: Usar Parallel.Invoke para executar operações paralelas | Descreve como usar Invokeo . |
Como: Retornar um valor de uma tarefa | Descreve como retornar valores de tarefas. |
Como: Cancelar uma tarefa e seus filhos | Descreve como cancelar tarefas. |
Como: Criar tarefas pré-computadas | Descreve como usar o Task.FromResult método para recuperar os resultados de operações de download assíncrono que são mantidas em um cache. |
Como: Percorrer uma árvore binária com tarefas paralelas | Descreve como usar tarefas para atravessar uma árvore binária. |
Como: Desembrulhar uma tarefa aninhada | Demonstra como usar o Unwrap método de extensão. |
Paralelismo de dados | Descreve como usar For e ForEach criar loops paralelos sobre dados. |
Programação paralela | Nó de nível superior para programação paralela do .NET Framework. |