有没有办法使用ContinueWith任务启动任务?

时间:2010-12-10 13:12:52

标签: c# .net exception-handling task-parallel-library

我的代码:

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

例外:

  

启动可能不会在继续任务上调用。

所以我需要开始第一项任务。有没有办法调用上一个任务Start方法来运行所有任务?

6 个答案:

答案 0 :(得分:7)

有任何理由不首先使用Task.Factory.StartNew msdnms docs 吗?是的,它是不一致的 - 但它从根本上讲是一种不同的任务,从明确开始而不仅仅是延续。

答案 1 :(得分:3)

我不确定写这个是什么问题:

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有什么问题?)为什么要寻找一种模糊的语法?

编辑:修复第一个例子......

编辑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的设计方式......我想我会坚持使用常规函数调用语法。

答案 2 :(得分:0)

问题是选择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!!

答案 3 :(得分:0)

我今天遇到了同样的问题。我想创建一个处理内部任务错误的包装器任务。这就是我想出的:

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;

这里的主要特点是 - 继续部分在原始任务之后运行,如您所愿 - 包装器是可启动的。使用ContineWith()创建的继续任务是nto Startable。

此示例的一个不太重要的特性是,异常被记录并丢弃(解决了我的问题,而不是你的问题)。当延续中发生异常时,您可能希望做一些不同的事情,例如将其重新抛出为当前任务的异常,以使其冒泡。

答案 4 :(得分:0)

据我所知,构建框架提供的非启动任务没有合理的方法。我能想到的最简单的解决方案是扩展方法。如果您需要此功能,可以使用以下示例。

警告:就像传递并组成大量的lambda一样,如果你发现自己需要这些,这通常意味着你在设计中缺少一种可以简化代码的类型。通过创建子任务来问问自己获得了什么。

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

答案 5 :(得分:0)

答案很简单。 ContinueWith自动启动任务。第一项任务需要运行。

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

因此,如果第一个任务未完成,则不会启动下一个任务。

你不需要开始继续阻止。