2013-05-27 14 views
7

Nel frammento di seguito, un'attività crea due attività figlio utilizzando TaskCreationOptions.AttachedToParent, il che significa che l'attività padre attenderà il completamento delle attività figlio.Valore di ritorno dalle attività padre-figlio

La domanda è: perché l'attività padre non restituisce il valore corretto [102]? Determina innanzitutto il suo valore di ritorno e quindi attende che le attività figlio finiscano. Se è così, allora qual è il punto di creare una relazione genitore-figlio?

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."); 
} 

L'output:

Main start. 
Calling Result. 
In parent start 
In parent end 
In child 1:101 
In child 2:102 
100 
Main end. 

risposta

5

Il problema è che si crea c1 e c2 come attività separate, ma poi torna subito i da t1 prima c1 e c2 hanno incrementato i.

Pertanto, il valore restituito da t1 viene catturato in quel punto ed è ancora 100.

Come hai notato, non c'è molto senso in una relazione genitore/figlio per questo accordo; ma ci sono un sacco di casi in cui è ha senso.

Un uso comune è giusto in modo che l'attività padre non venga completata fino a quando le sue attività figlio non sono state completate, ma se si richiede all'attività padre di attendere i propri figli prima di restituire un valore, non sarà possibile farlo come Questo.

Naturalmente, è possibile risolvere il problema con l'aggiunta di

Task.WaitAll(c1, c2); 

poco prima della return i;. So che non è quello che stai chiedendo, ma volevo solo farlo notare comunque.

+0

Sì, attendere e restituire il valore sono cose diverse, ma perché non aspettare prima di decidere il valore restituito. Pensi che questo potrebbe essere un bug in TPL? – thewpfguy

+0

@thewpfguy No, non è sicuramente un bug. È una conseguenza naturale del fatto che il risultato di un'attività viene memorizzato nella cache se esce prima che l'attività che ha avviato la prima attività acceda al valore di ritorno della prima attività. –

-1

Come già detto, il valore di i viene restituito prima che venga incrementato. cambiando il codice in questo modo restituire il valore atteso (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."); 
} 

quello che ho fatto è semplicemente estrarre il Thread.Sleep (1000) dal compito bambino per l'attività principale. Il risultato viene restituito ora dopo che la variabile è stata incrementata.

+2

Questa è una risposta terribile IMO. I posti letto sono stati aggiunti alle attività figlio per simulare un lungo lavoro. Spostandoli verso il compito principale è il meccanismo di sincronizzazione di pover'uomo. Certo che funzionerà, a meno che i tak dei bambini non prendano più di 1000ms per completare. Dato che abbiamo molti, molti modi per sincronizzare i dati in .NET - usare sleep per farlo è la peggiore soluzione possibile che non dovresti mai usare – Pako

+0

@Pako Sono d'accordo con te ma volevo solo far notare perché la variabile int non era in fase di aggiornamento alla fine delle due attività. – codingadventures