Подтвердить что ты не робот

Есть ли способ запустить задачу с помощью задачи ContinueWith?

Мой код:

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();

Исключение:

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

Итак, мне нужно запустить первую задачу. Есть ли способ вызвать последнюю задачу? Начать метод для запуска всех задач?

4b9b3361

Ответ 1

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

Ответ 2

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

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 спроектирован... Я думаю, что я бы придерживался синтаксиса регулярных функций.

Ответ 3

Проблема заключается в том, что выбор задач с помощью 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!!

Ответ 4

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

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(), являются nto Startable.

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

Ответ 5

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

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

/// <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);
            });
    }
}

Ответ 6

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

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

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

 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();
        } );

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

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