Поделиться через


Практическое руководство. Сцепление нескольких задач с помощью продолжений

В библиотеке параллельных задач задача, метод 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 и библиотеке параллельных задач

Другие ресурсы

Задачи продолжения

Параллелизм задач (библиотека параллельных задач)