2013-05-27 46 views
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. 

回答

5

的问题是,您创建c1c2作为单独的任务,但随后c1c2已递增i之前立即从t1返回i

因此,从t1返回的值在该点被捕获,并且仍然是100

正如你所指出的,这种安排在父母/子女关系方面没有多少意义;但有很多情况下它确实是有意义的。

一个常用的用法是,父任务直到它的子任务完成才会完成,但是如果你需要父任务在返回一个值之前等待它的子节点,你将无法做到这一点这个。

当然,你也可以仅由return i;之前添加

Task.WaitAll(c1, c2); 

修复它。我知道这不是你要问的,但我只是想指出。

+0

是的等待和返回值是不同的东西,但为什么不等待决定返回值。你认为这可能是TPL中的一个错误吗? – thewpfguy

+0

@thewpfguy不,它绝对不是一个错误。这是事实的一个自然结果,如果任务的结果在启动第一个任务的任务访问第一个任务的返回值之前退出,则结果将被缓存。 –

-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

这是IMO的糟糕答案。睡眠被添加到儿童任务来模拟长时间的工作。将他们转移到主要任务是穷人的同步机制。当然它会起作用,除非孩子接受超过1000毫秒才能完成。鉴于我们有许多方法来同步.NET中的数据 - 使用睡眠来做到这一点是最不可能使用的最糟糕的解决方案 – Pako

+0

@Pako我同意你的看法,但我只想指出为什么int变量不是在两项任务结束时进行更新。 – codingadventures