2013-05-27 12 views
7

以下のスニペットでは、TaskCreationOptions.AttachedToParentを使用して2つの子タスクを作成します。つまり、親タスクは子タスクが完了するまで待機します。親子タスクからの戻り値

質問です - なぜ親タスクは正しい値[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を作成したが、その後c1t1c2からすぐiを返す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変数が2つのタスクの最後に更新されます。 – codingadventures

関連する問題