Практическое руководство. Сцепление нескольких задач с помощью продолжений
В библиотеке параллельных задач задача, метод ContinueWith которой вызывается, называется предшествующей, а задача, определенная в методе ContinueWith, называется продолжением. В этом примере показано использование методов ContinueWith и ContinueWith классов Task и Task<TResult> для указания задачи, которая запускается после завершения предшествующей задачи.
В примере также показано, как указать продолжение, которое будет запускаться, только если его предшествующая задача отменена.
В этих примерах демонстрируется, как продолжить выполнение с одной задачи. Можно также создать продолжение, которое будет запускаться после завершения или отмены любой или всех групп задач. Дополнительные сведения см. в разделах TaskContinueWhenAll() и TaskContinueWhenAny().
Пример
В методе DoSimpleContinuation показан базовый синтаксис для метода ContinueWith. Обратите внимание, что предшествующая задача предоставляется в качестве входного параметра для лямбда-выражения в методе ContinueWith. Это позволяет оценить состояние предшествующей задачи до выполнения работ в продолжении. Используйте эту простую перегрузку метода ContinueWith, когда нет необходимости передавать какое-либо состояние из одной задачи в другую.
В методе DoSimpleContinuationWithState показано, как использовать перегрузку ContinueWith для передачи результата из предшествующей задачи в задачу продолжения.
Imports System.IO
Imports System.Threading
Imports System.Threading.Tasks
Module ContinueWith
Sub Main()
DoSimpleContinuation()
Console.WriteLine("Press any key to exit")
Console.ReadKey()
End Sub
Sub DoSimpleContinuation()
Dim path As String = "C:\users\public\TPLTestFolder\"
Try
Dim firstTask = New Task(Sub() CopyDataIntoTempFolder(path))
Dim secondTask = firstTask.ContinueWith(Sub(t) CreateSummaryFile(path))
firstTask.Start()
Catch e As AggregateException
Console.WriteLine(e.Message)
End Try
End Sub
' A toy function to simulate a workload
Sub CopyDataIntoTempFolder(ByVal path__1 As String)
System.IO.Directory.CreateDirectory(path__1)
Dim rand As New Random()
For x As Integer = 0 To 49
Dim bytes As Byte() = New Byte(999) {}
rand.NextBytes(bytes)
Dim filename As String = Path.GetRandomFileName()
Dim filepath As String = Path.Combine(path__1, filename)
System.IO.File.WriteAllBytes(filepath, bytes)
Next
End Sub
Sub CreateSummaryFile(ByVal path__1 As String)
Dim files As String() = System.IO.Directory.GetFiles(path__1)
Parallel.ForEach(files, Sub(file)
Thread.SpinWait(5000)
End Sub)
System.IO.File.WriteAllText(Path.Combine(path__1, "__SummaryFile.txt"), "did my work")
Console.WriteLine("Done with task2")
End Sub
Sub DoSimpleContinuationWithState()
Dim nums As Integer() = {19, 17, 21, 4, 13, 8, _
12, 7, 3, 5}
Dim f0 = New Task(Of Double)(Function() nums.Average())
Dim f1 = f0.ContinueWith(Function(t) GetStandardDeviation(nums, t.Result))
f0.Start()
Console.WriteLine("the standard deviation is {0}", f1)
End Sub
Function GetStandardDeviation(ByVal values As Integer(), ByVal mean As Double) As Double
Dim d As Double = 0.0R
For Each n In values
d += Math.Pow(mean - n, 2)
Next
Return Math.Sqrt(d / (values.Length - 1))
End Function
End Module
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace ContinueWith
{
class Continuations
{
static void Main()
{
SimpleContinuation();
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
static void SimpleContinuation()
{
string path = @"C:\users\public\TPLTestFolder\";
try
{
var firstTask = new Task(() => CopyDataIntoTempFolder(path));
var secondTask = firstTask.ContinueWith((t) => CreateSummaryFile(path));
firstTask.Start();
}
catch (AggregateException e)
{
Console.WriteLine(e.Message);
}
}
// A toy function to simulate a workload
static void CopyDataIntoTempFolder(string path)
{
System.IO.Directory.CreateDirectory(path);
Random rand = new Random();
for (int x = 0; x < 50; x++)
{
byte[] bytes = new byte[1000];
rand.NextBytes(bytes);
string filename = Path.GetRandomFileName();
string filepath = Path.Combine(path, filename);
System.IO.File.WriteAllBytes(filepath, bytes);
}
}
static void CreateSummaryFile(string path)
{
string[] files = System.IO.Directory.GetFiles(path);
Parallel.ForEach(files, (file) =>
{
Thread.SpinWait(5000);
});
System.IO.File.WriteAllText(Path.Combine(path, "__SummaryFile.txt"), "did my work");
Console.WriteLine("Done with task2");
}
static void SimpleContinuationWithState()
{
int[] nums = { 19, 17, 21, 4, 13, 8, 12, 7, 3, 5 };
var f0 = new Task<double>(() => nums.Average());
var f1 = f0.ContinueWith( t => GetStandardDeviation(nums, t.Result));
f0.Start();
Console.WriteLine("the standard deviation is {0}", f1.Result);
}
private static double GetStandardDeviation(int[] values, double mean)
{
double d = 0.0;
foreach (var n in values)
{
d += Math.Pow(mean - n, 2);
}
return Math.Sqrt(d / (values.Length - 1));
}
}
}
Параметр типа Task<TResult> определяет возвращаемый тип делегата. Возвращаемое значение передается в задачу продолжения. Таким образом можно связывать друг с другом произвольное число задач.
См. также
Основные понятия
Параллельное программирование в .NET Framework
Лямбда-выражения в PLINQ и библиотеке параллельных задач