2009-12-17 11 views
5

Wenn ich den Code unten ausführen, ist die Ausgabe "DelegateDisplayIt", in der Regel 1-4 mal wiederholt. Ich habe diesen Code wahrscheinlich 100 Mal ausgeführt, und nicht einmal die Ausgabe war jemals "ParameterizedDisplayIt". Es scheint also die Art und Weise, in der der Thread erstellt und anschließend gestartet wird, wie der Parameter übergeben wird. Beim Erstellen eines neuen Threads mit einem anonymen Delegaten ist der Parameter ein Verweis auf die ursprüngliche Variable. Wenn er jedoch mit einem ParameterizedThreadStart-Delegat erstellt wird, ist der Parameter ein völlig neues Objekt. Scheint meine Annahme richtig? Wenn dem so ist, scheint das ein seltsamer Nebeneffekt des Thread-Konstruktors zu sein, nicht?Unterschiedliches Verhalten beim Starten eines Threads: ParameterizedThreadStart vs. Anonymous Delegate. Warum spielt es eine Rolle?

static void Main() 
{ 
    for (int i = 0; i < 10; i++) 
    { 
     bool flag = false; 

     new Thread(delegate() { DelegateDisplayIt(flag); }).Start(); 

     var parameterizedThread = new Thread(ParameterizedDisplayIt); 
     parameterizedThread.Start(flag); 

     flag = true; 
    } 

    Console.ReadKey(); 
} 

private static void DelegateDisplayIt(object flag) 
{ 
    if ((bool)flag) 
     Console.WriteLine("DelegateDisplayIt"); 
} 

private static void ParameterizedDisplayIt(object flag) 
{ 
    if ((bool)flag) 
     Console.WriteLine("ParameterizedDisplayIt"); 
} 

Antwort

5

Ihre Annahme ist richtig. Die Anweisung kopiert die Flag-Variable zum Zeitpunkt des Aufrufs.

Im Gegensatz dazu erfasst der anonyme Delegat die ursprüngliche Variable in einer closure. Die Variable wird erst kopiert, wenn der Delegat die Methode ausführt. An diesem Punkt kann der Wert wahr oder false sein, abhängig davon, wo sich der ursprüngliche Thread in der aufrufenden Schleife befindet.

1

Lassen Sie uns den ersten Fall nehmen:

for (int i = 0; i < 10; i++) 
{ 
    bool flag = false; 
    new Thread(delegate() { DelegateDisplayIt(flag); }).Start(); 
    flag = true; 
} 

Hier, wenn Sie den anonymen Delegaten Konstrukt der Wert der Flagge falsch ist, aber wenn die DelegateDisplayIt Verfahren ausführt, hat das Flag bereits auf true gesetzt worden durch die nächste Zeile, und Sie sehen die Ausgabe angezeigt. Hier ist ein anderes Beispiel, das das gleiche Konzept veranschaulicht:

Dies wird fünf mal fünf drucken.

Nun wollen wir den zweiten Fall nehmen:

for (int i = 0; i < 10; i++) 
{ 
    bool flag = false; 
    var parameterizedThread = new Thread(ParameterizedDisplayIt); 
    parameterizedThread.Start(flag);   
    flag = true; 
} 

die an die Callback übergebene Wert ist derjenige, der die Variable besitzen, wenn Sie die Start Methode aufrufen, die false ist und das ist, warum Sie nie die Ausgabe sehen in die Konsole.

+1

Mit anderen Worten, Delegaten sind an Namen in ihren Bereichen gebunden, nicht an die Werte? Und ihre Parameter werden nur ausgewertet, wenn der Delegat tatsächlich aufgerufen wird, nicht wenn der Delegat selbst erstellt wird? –

+0

Genau, du hast es großartig gemacht, es zu synthetisieren.Mein Englisch ist sehr schlecht :-( –

+0

In Anbetracht Ihrer Reputation, Ihr Englisch ist genial :-) Allerdings schlagen andere Antworten vor, dies gilt nur für anonyme Delegierte. Kompliziert: -/ –

4

flag ist eine boolesche Variable. Es wird als Wert an den Delegaten übergeben. Es wird nie wahr sein, weil der falsche Wert kopiert und an den Delegaten gesendet wird.

Wenn Sie einen anonymen Delegaten verwenden, verwenden Sie implizit einen closure, um auf den booleschen Wert zuzugreifen. Der .NET-Compiler erstellt einen anonymen Typ, der die Referenz für die Variable enthält, die das Objekt der Closure ist (Flag), auf die dann sowohl der anonyme Delegat als auch die Methode verweisen. Sie werden dann die Variable teilen, wenn sie sich also ändern, sehen beide die Änderung.

Dies ist wirklich keine Threading-Frage, es geht um Vorbeifahrt-Wert und Schließungen. Hier ist ein decent blog post about closures. Der Preis-pro-Kopf-Wert ist ziemlich einfach. Wenn Sie auffrischen müssen, würde ich vorschlagen, eine Kopie von CLR Via C# von Jeffrey Richter zu kaufen.

Verwandte Themen