从父子任务中返回值

7
在下面的代码片段中,一个任务使用TaskCreationOptions.AttachedToParent创建了两个子任务,这意味着父任务将等待子任务完成。
问题是:为什么父任务没有返回正确的值[102]?它是否首先确定其返回值,然后等待子任务完成。如果是这样,那么创建父子关系有什么意义?
void Main()
{
Console.WriteLine ("Main start.");
int i = 100;

Task<int> t1 = new Task<int>(()=> 
{
    Console.WriteLine ("In parent start");
    Task c1 = Task.Factory.StartNew(() => {
        Thread.Sleep(1000);
        Interlocked.Increment(ref i);
        Console.WriteLine ("In child 1:" + i);
    }, TaskCreationOptions.AttachedToParent);

    Task c2 = Task.Factory.StartNew(() => {
        Thread.Sleep(2000);
        Interlocked.Increment(ref i);           
        Console.WriteLine ("In child 2:" + i);
    }, TaskCreationOptions.AttachedToParent );

    Console.WriteLine ("In parent end");
    return i;
}); 

t1.Start();
Console.WriteLine ("Calling Result.");
Console.WriteLine (t1.Result);
Console.WriteLine ("Main end.");
}

输出结果:
Main start.
Calling Result.
In parent start
In parent end
In child 1:101
In child 2:102
100
Main end.
2个回答

5
问题在于你将和作为单独的任务创建,但是在和增加之前,立即从返回

因此,的返回值在那一点被捕获,并且仍然是100。
正如你所指出的,对于这种安排,父/子关系没有太多意义;但是有很多情况下确实是有意义的。
常见用途只是为了使父任务在其子任务完成之前不会完成,但如果您要求父任务在返回值之前等待其子任务,则无法像这样执行。
当然,您可以通过添加来解决它。
Task.WaitAll(c1, c2);

return i;之前,我知道这不是你所问的,但我想无论如何都要指出来。

是的,等待和返回值是不同的事情,但为什么不在决定返回值之前等待呢?您认为这可能是TPL中的一个错误吗? - thewpfguy
@thewpfguy 不,这绝对不是一个错误。这是一个自然的结果,因为如果任务的结果在第一个任务启动的任务访问第一个任务的返回值之前退出,则任务的结果会被缓存。 - Matthew Watson

-1

正如已经说过的那样,在增加i的值之前,它的值被返回。将您的代码更改为以下方式即可返回预期值(102):

void Main()
{
    Console.WriteLine ("Main start.");
    int i = 100;

    Task<int> t1 = new Task<int>(()=> 
    {


    Console.WriteLine ("In parent start");
    Task c1 = Task.Factory.StartNew(() => {
        Interlocked.Increment(ref i);
        Console.WriteLine ("In child 1:" + i);
    }, TaskCreationOptions.AttachedToParent);

    Thread.Sleep(1000);

    Task c2 = Task.Factory.StartNew(() => {
        Interlocked.Increment(ref i);           
        Console.WriteLine ("In child 2:" + i);
    }, TaskCreationOptions.AttachedToParent );

    Thread.Sleep(1000);

    Console.WriteLine ("In parent end");
    return i;
}); 

t1.Start();
Console.WriteLine ("Calling Result.");
Console.WriteLine (t1.Result);
Console.WriteLine ("Main end.");
 }

我所做的就是将子任务中的Thread.Sleep(1000)从子任务中移除到父任务中。现在,在变量递增后返回结果。

2
在我看来,这是一个糟糕的答案。将睡眠添加到子任务中以模拟长时间工作。将它们移动到主任务是一种贫民的同步机制。当然,它会起作用,除非子任务需要超过1000毫秒才能完成。鉴于我们在.NET中有许多许多同步数据的方法-使用sleep来实现这一点是最糟糕的解决方案,您永远不应该使用。 - Jarek
@Pako 我同意你的观点,但我只是想指出为什么 int 变量在两个任务结束时没有被更新。 - codingadventures

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接