如何:用延续将多个任务链接在一起

在任务并行库中,其 ContinueWith 方法将被调用的任务称为前面的任务,而 ContinueWith 方法中定义的任务称为延续。 此示例演示如何使用 TaskTask<TResult> 类的 ContinueWithContinueWith 方法指定在前面的任务完成后开始的任务。

示例还演示如何指定仅在其前面的任务被取消时才运行的延续。

这些示例演示如何从单一任务中继续。 您也可以创建在一组任务中的任意任务或全部任务完成或被取消后运行的延续。 有关更多信息,请参见 TaskContinueWhenAll() 和 TaskContinueWhenAny()。

示例

DoSimpleContinuation 方法演示 ContinueWith 的基本语法。 请注意,会将前面的任务作为输入参数提供给 ContinueWith 方法中的 lambda 表达式。 这样,您将能够在前面的任务执行延续中的任何工作前计算其状态。 在不必将任何状态从一个任务传递到另一个任务时,请使用 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 和 TPL 中的 Lambda 表达式

其他资源

延续任务

任务并行(任务并行库)