2012-04-03 7 views
4

Der Kodex

using System; 
using System.Threading; 

public delegate void LoadingProgressCallback(double PercentComplete,string ItemName); 
public delegate void LoadCompleteCallback(int ItemID, string ItemName); 

public static class Program 
{ 
    public static void Main(string[] args) 
    { 
     LoadTest loadTest = new LoadTest(); 
     loadTest.LoadItems(args); 
    } 
} 

public class LoadTest 
{  
    ManualResetEvent resetEvent; 
    int numThreads = 0; 

    public LoadTest() 
    {} 

    public void LoadItems(string[] Items) 
    { 
     numThreads = 0; 
     resetEvent = new ManualResetEvent(false); 

     foreach(string item in Items) 
     { 
      Console.WriteLine("Adding {0} to ThreadPool",item); 
      ThreadPool.QueueUserWorkItem 
      (
       delegate 
       { 
        Load(item, this.progCall, this.compCall); 
       } 
      ); 
      numThreads++; 

      Thread.Sleep(100);//Remove this line 

     } 
     resetEvent.WaitOne(); 
    } 

    public void progCall(double PercentComplete, string ItemName) 
    { 
     Console.WriteLine("{0}: is {1}% Complete [THREAD:{2}]",ItemName,PercentComplete.ToString(),Thread.CurrentThread.ManagedThreadId.ToString()); 
    } 
    public void compCall(int ItemID, string ItemName) 
    { 
     Console.WriteLine("{0}: is Complete",ItemName); 
     numThreads--; 
     if(numThreads == 0) 
     { 
      resetEvent.Set(); 
     } 
    } 

    public void Load(string Item, LoadingProgressCallback progressCallback, LoadCompleteCallback completeCallback) 
    { 
     Console.WriteLine("Loading: {0} [THREAD:{1}]",Item,Thread.CurrentThread.ManagedThreadId.ToString()); 

     for(int i = 0; i <= 100; i++) 
     { 
      if(progressCallback != null) 
      { 
       progressCallback((double)i, Item); 
      } 
      Thread.Sleep(100); 
     } 
     if(completeCallback != null) 
     { 
      completeCallback(0,Item); 
     } 
    } 
} 

Beobachtung

Wenn ich dieses Programm von der Befehlszeile ausführen, wie dieser ...Kann jemand dieses seltsame Verhalten bei der Arbeit mit ThreadPool erklären?

>TheProgram item1 item2

Die Ausgabe wird wie folgt aussehen.

Hinzufügen item1 zu Thread
Laden: item1 [Thema: 3]
item1: 0% komplett [THREAD: 3]
Hinzufügen Element2 zu Thread
Laden: Element2 [Thema: 4]
Element2: ist 0% fertig [Thema: 4]
item1: 1% Komplettes [Thema: 3] ist
Element2: 1% abgeschlossen [Thema: 4]
item1: 2% abgeschlossen [Thema: 3]
item2: 2% abgeschlossen [Thema: 4]

Allerdings, wenn ich diese Zeile entfernen.

Thread.Sleep(100);//Remove this line

Vom LoadItems Verfahren sieht die Ausgabe wie folgt aus.

Hinzufügen item1 zu Thread
Hinzufügen Element2 zu Thread
Laden: Element2 [Thema: 4]
Laden: Element2 [Thema: 3]
Element2: ist 0% fertig [Thema: 4]
Element2: ist 0% fertig [Thema: 3]
Element2: 1% Komplettes [Thema: 4] ist
Element2: 1% abgeschlossen [Thema: 3]
Element2: 2% komplett [Thema: 3 ]
i TEM2: 2% abgeschlossen [Thema: 4]

Die Frage

Es scheint, als ob zwei Threads verwendet werden, obwohl sie beide scheinen auf den gleichen Daten handeln werden. Warum verhält sich der Code so?

Antwort

8

Sie schließen die Schleifenvariable, was zu einem unerwarteten Ergebnis führt.Versuchen Sie stattdessen:

foreach(string item in Items) 
{ 
    string item2 = item; 
    Console.WriteLine("Adding {0} to ThreadPool", item2); 
    ThreadPool.QueueUserWorkItem 
    (
     delegate 
     { 
      Load(item2, this.progCall, this.compCall); 
     } 
    ); 
    numThreads++; 

    Thread.Sleep(100);//Remove this line 

} 

Referenzen

+0

+1 Guter Fang, ich den Code für 5 Minuten starrte und einfach sah es nicht. –

+0

+1. Es müssen mindestens 500 Fragen zu diesem Thema allein sein. Ich habe bis jetzt ein paar beantwortet. – Aliostad

3

Eine Sache, die sofort Blick auf den Code in den Sinn kommt ist der Mangel an Einsatz von Interlocked.

Sie müssen it verwenden, sonst sehen Sie seltsame Fehler und Verhaltensweisen.

Also statt

numThreads++; 

Verwendung:

Interlocked.Increment(ref numThreads); 
+0

Würde ich dann 'Interlocked.Decrement (ref numThreads) 'in der' compCall' Methode aufrufen? – Tester101

+0

@ Tester101 ja natürlich ** inklusive Dekrement **. – Aliostad

Verwandte Themen