【问题标题】:Returning value from Parent-Child tasks从父子任务返回值
【发布时间】:2013-05-27 10:08:52
【问题描述】:

在下面的 sn-p 中,一个任务使用 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.

【问题讨论】:

    标签: c# task-parallel-library


    【解决方案1】:

    问题是您将c1c2 创建为单独的任务,然后在c1c2 递增i 之前立即从t1 返回i

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

    正如您所指出的,这种安排在父/子关系中没有多大意义;但在很多情况下它确实有意义。

    一个常见的用法是让父任务在其子任务完成之前不完成,但如果您要求父任务在返回值之前等待其子任务,您将无法这样做.

    当然,你可以通过添加来修复它

    Task.WaitAll(c1, c2);
    

    就在return i; 之前。我知道这不是你要问的,但我还是想指出这一点。

    【讨论】:

    • 是的,等待和返回值是不同的东西,但是为什么不在决定返回值之前等待。您认为这可能是 TPL 中的错误吗?
    • @thewpfguy 不,这绝对不是错误。这是一个自然的结果,如果一个任务的结果在启动第一个任务的任务访问第一个任务的返回值之前退出,它就会被缓存。
    【解决方案2】:

    如前所述,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) 从子任务中取出到父任务。变量递增后立即返回结果。

    【讨论】:

    • 这是 IMO 糟糕的答案。睡眠被添加到子任务中以模拟长时间的工作。将它们转移到主要任务是穷人的同步机制。当然它会起作用,除非子任务需要超过 1000 毫秒才能完成。鉴于我们有很多很多方法可以在 .NET 中同步数据 - 使用睡眠来执行此操作是您永远不应该使用的最糟糕的解决方案
    • @Pako 我同意你的观点,但我只是想指出为什么 int 变量在两个任务结束时没有更新。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多