Есть ли способ запустить задачу, используя задачу ContinueWith? - PullRequest
20 голосов
/ 10 декабря 2010

Мой код:

var r = from x in new Task<int>(() => 1)
        from y in new Task<int>(() => x + 1) 
        select y;
r.ContinueWith(x => Console.WriteLine(x.Result)).Start();   

или

new Task<int>(() => 1)
    .ContinueWith(x => x.Result + 1)
    .ContinueWith(x => Console.WriteLine(x.Result))
    .Start();

Исключение:

Старт не может быть вызван для задачи продолжения.

Так что мне нужно запустить первое задание. Есть ли способ вызвать метод запуска последней задачи, чтобы запустить все задачи?

Ответы [ 6 ]

7 голосов
/ 10 декабря 2010

Есть ли основания не использовать Task.Factory.StartNew msdn , ms docs для первого задания? Да, это противоречиво - но это принципиально иная задача, с точки зрения того, что она запускается явно, а не просто как продолжение.

3 голосов
/ 10 декабря 2010

Я не совсем уверен, что не так с простым написанием этого:

var t1 = new Task<int>(() => 1)
var r = from x in t1
        from y in new Task<int>(() => x + 1) 
        select y;
r.ContinueWith(x => Console.WriteLine(x.Result));
t1.Start();

или этого:

var t = new Task<int>(() => 1)
t.ContinueWith(x => x.Result + 1)
 .ContinueWith(x => Console.WriteLine(x.Result))
t.Start();

Это прямо выражает то, что вы действительно хотите сделать.(Это начальное задание, которое вы хотите запустить. Так что же не так с вызовом Start для этого начального задания?) Почему вы ищете синтаксис, который затемняет это?

EDIT : исправлен первый пример ...

РЕДАКТИРОВАТЬ 2 , чтобы добавить:

Итак, теперь я понимаю, что LinqToTasks ожидает, что селекторы задач вернут запущенные задачи.Итак, второе предложение from в вашем первом примере возвращает задачу, которая никогда не будет выполняться.Итак, что вам действительно нужно, это:

var t1 = new Task<int>(() => 1);
var r = from x in t1
        from y in Task<int>.Factory.StartNew(() => x + 1)
        select y;
r.ContinueWith(x => Console.WriteLine(x.Result));
t1.Start();

Ничто другое не будет вызывать Start для задач, созданных в этих пунктах from.Поскольку соответствующие селекторы на самом деле не выполняются до тех пор, пока не завершится предыдущая задача, вы по-прежнему контролируете, когда запускать корневую задачу.

Кажется, что это работает, но это довольно уродливо.Но похоже, что именно так разработан LinqToTasks ... Я думаю, что я бы придерживался обычного синтаксиса вызова функций.

0 голосов
/ 30 июня 2017

Ответ прост.Continue с автоматически запускаемой задачей.И первая задача должна быть запущена.

var r= Task<int>.Run<int>( () => 1 )
             .ContinueWith<int>( x => x.Result + 1 )
             .ContinueWith( x => Console.WriteLine( x.Result ) );

ContinueWith возвращаемой задачи, которая начинается с проверки выполненной предыдущей задачи или нет.Этот код работает так же, как показано ниже:

 var firstTask = new Task<int>( () => 1 );
        firstTask.Start();
        var firstawaiter = firstTask.GetAwaiter();
        var secondTask = new Task<int>( x => (int)x + 1 , firstawaiter.GetResult());

        firstawaiter.OnCompleted( () =>
        {
            secondTask.Start();
        } );

        var secondawaiter = secondTask.GetAwaiter();


        var thirdTask = new Task( x => Console.WriteLine( x ) , secondawaiter.GetResult());

        secondawaiter.OnCompleted( () =>
        {
            thirdTask.Start();
        } );

Итак, если первая задача не выполнена, следующая задача не будет запущена.

И вам не нужно начинать непрерывно с блока.

0 голосов
/ 03 сентября 2015

Насколько я знаю, не существует разумного способа составления незапущенных задач, предоставляемых фреймворком. Самое простое решение, которое я могу придумать, - это методы расширения. Вот несколько примеров, которые вы можете использовать, если вам нужна эта функциональность.

Предупреждение : Точно так же, как при раздаче и составлении тонны лямбд, если вам это нужно, это часто означает, что в вашем дизайне отсутствует тип, упрощающий ваш код. Спросите себя, что вы получили, создавая подзадачи.

/// <summary>
/// Compose tasks without starting them.
/// Waiting on the returned task waits for both components to complete.
/// An exception in the first task will stop the second task running.
/// </summary>
public static class TaskExtensions
{
    public static Task FollowedBy(this Task first, Task second)
    {
        return FollowedBy(first,
            () =>
            {
                second.Start();
                second.Wait();
            });
    }

    public static Task FollowedBy(this Task first, Action second)
    {
        return new Task(
            () =>
            {
                if (first.Status == TaskStatus.Created) first.Start();
                first.Wait();
                second();
            });
    }

    public static Task FollowedBy<T>(this Task first, Task<T> second)
    {
        return new Task<T>(
            () =>
            {
                if (first.Status == TaskStatus.Created) first.Start();
                first.Wait();
                second.Start();
                return second.Result;
            });
    }

    public static Task FollowedBy<T>(this Task<T> first, Action<T> second)
    {
        return new Task(
            () =>
            {
                if (first.Status == TaskStatus.Created) first.Start();
                var firstResult = first.Result;
                second(firstResult);
            });
    }

    public static Task<TSecond> FollowedBy<TFirst, TSecond>(this Task<TFirst> first, Func<TFirst, TSecond> second)
    {
        return new Task<TSecond>(
            () =>
            {
                if (first.Status == TaskStatus.Created) first.Start();
                return second(first.Result);
            });
    }
}
0 голосов
/ 17 января 2013

У меня была такая же проблема сегодня. Я хотел создать задачу-оболочку, которая обрабатывает ошибку из внутренней задачи. Вот что я придумал:

var yourInitialTask = new Task(delegate
{
    throw e;
});

var continuation = task.ContinueWith(t =>
{
    if (task.IsCanceled)
    {
        Debug.WriteLine("IsCanceled: " + job.GetType());
    }
    else if (task.IsFaulted)
    {
        Debug.WriteLine("IsFaulted: " + job.GetType());
    }
    else if (task.IsCompleted)
    {
        Debug.WriteLine("IsCompleted: " + job.GetType());
    }
}, TaskContinuationOptions.ExecuteSynchronously); //or consider removing execute synchronously if your continuation task is going to take long

var wrapper = new Task(() =>
{
   task.Start();
   continuation.Wait();
});

return wrapper;

Ключевые особенности здесь - продолжение продолжается после оригинальной задачи, как вы хотите -обертка Startable. Задачи продолжения, созданные с помощью ContineWith(), не запускаются.

Менее ключевой особенностью этого примера является то, что исключение регистрируется и удаляется (решает мою проблему, а не вашу). Возможно, вы захотите сделать что-то другое, когда в продолжении возникнут исключения, например, сбросить его как исключение из текущей задачи, чтобы оно выпало.

0 голосов
/ 15 июля 2011

Проблема в том, что выбор задач с помощью LINQ только создаст дерево выражений!

Итак, вот что вам нужно сделать:

var query = 
    from i in Enumerable.Range(1, 4) 
    let task = Task.Factory.StartNew(() => Tuple.Create(i, IsPrime(i))) // put a breakpoint here
    select task.ContinueWith(delegate {
        Console.WriteLine("{0} {1} prime.", _.Result.Item1, _.Result.Item2 ? "is" : "is not");
    });
// breakpoint never hit yet
query.ToArray(); // breakpoint hit here 4 times
// all tasks are now running and continuations will start
TaskEx.Await(query.ToArray()); // breakpoint hit 4 more times!!
...