链接不同返回类型的任务

时间:2012-06-24 17:56:20

标签: c# .net .net-4.0 task-parallel-library

是否可以链接具有不同返回类型或根本没有返回类型的任务?例如,在伪代码中:

Task<double>.ContinueWith(Task<string>).ContinueWith(Task<String>).ContinueWith(Task)

或者这里也是真实的代码示例:

private double SumRootN(int root)
{
    double result = 0;
    for (int i = 1; i < 10000000; i++)
    {
        result += Math.Exp(Math.Log(i) / root);
    }
    return result;
}

private void taskSequentialContinuationButton_Click(object sender, RoutedEventArgs e)
{
    Task<double> task = null;

    this.statusText.Text = "";
    this.watch = Stopwatch.StartNew();
    for (int i = 2; i < 20; i++)
    {
        int j = i;
        if (task == null)
        {
            task = Task<double>.Factory.StartNew(() => { return SumRootN(j); });
        }
        else
        {
            task = task.ContinueWith((t) => { return SumRootN(j); });
        }

        task = task.ContinueWith((t) => 
        { // I don't want to return anything from this task but I have to, to get it to compile
            this.statusText.Text += String.Format("root {0} : {1}\n", j, t.Result);
            return t.Result;
        }, TaskScheduler.FromCurrentSynchronizationContext());
    }

    task.ContinueWith((t) => 
        { // I also don't want to return anything here but I don't seem to have to here even though intellisense expects a Task<double>??
            this.statusText.Text += String.Format("{0}ms elapsed\n", watch.ElapsedMilliseconds);
        }, TaskScheduler.FromCurrentSynchronizationContext());
}

请参阅内联注释以了解链接的奇怪之处。

2 个答案:

答案 0 :(得分:3)

如果你想用不同的返回类型链接Task,你可以把它们放在一个不同的变量中:

Task<Type1> task1 = Task.Factory.StartNew(() => Compute1());
Task<Type2> task2 = task1.ContinueWith(_ => Compute2());
Task<Type3> task3 = task2.ContinueWith(_ => Compute3());

对于您的特定情况,您在循环中计算某些内容并希望在每次迭代后报告UI线程,您可以这样做:

var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();

Task.Factory.StartNew(() =>
{
    for (int i = 2; i < 20; i++)
    {
        // perform computation

        Task.Factory.StartNew(() =>
        {
            // report result
        }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
    }
});

答案 1 :(得分:0)

您重复使用单个task变量会使代码变得复杂。看起来您想要启动一些任务并等待它们完成。这样的事情应该有效:

SynchronizationContext context = SynchronizationContext.Current;

Task[] tasks = Enumerable.Range(2, 19)
    .Select(d => Task<double>.Factory.StartNew(() => SumRootN(d))
        .ContinueWith(t => {
            this.statusText.Text += string.Format("root {0} : {1}\n", d, t.Result);
        }))
    .ToArray();

Task.Factory.StartNew(() => {
    Task.WaitForAll(tasks);
    string msg = string.Format("{0}ms elapsed\n", watch.ElapsedMilliseconds);
    context.Post(_ => { this.statusText.Text += msg; }, null);
});

编辑:如果你想创建一系列任务,这可能有效:

Task first = new Task(() => { });
Task outer = Enumerable.Range(2, 19)
    .Aggregate(first, (task, d) => {
        Task inner = new Task<double>(() => SumRootN(d))
            .ContinueWith(rt => {
                this.statusText.Text += String.Format("root {0} : {1}\n", d, rt.Result);
            });
        return task.ContinueWith(inner);
    });

outer.ContinueWith(t => {
    this.statusText.Text += String.Format("{0}ms elapsed\n", watch.ElapsedMilliseconds);
});

first.Start();