2009-08-14 17 views
6

Ho il seguente codice:C# Threading/Blocco confusione

var items = new List<string> {"1", "2", "3"}; // 200 items 
foreach(var item in items) { 
    ThreadPool.QueueUserWorkItem((DoWork), item); 
} 

private void DoWork(object obj) 
{ 
    lock(this) 
    { 
    using(var sw = File.AppendText(@"C:\somepath.txt") 
    { 
     sw.WriteLine(obj); 
    } 
    } 
} 

A causa della filettatura, per qualche motivo, ho un numero casuale di 200 articoli scritti fuori al file. 60 o 127 o talvolta solo 3. Se rimuovo il ThreadPool e scrivo solo all'interno del ciclo foreach originale, tutti i 200 elementi sono scritti correttamente.

Non sai perché questo si verifica?

Grazie per il vostro aiuto.

risposta

9

La seguente nota da MSDN documentation on ThreadPool dice tutto:

I thread nel pool di thread gestito sono thread in background. Cioè, le loro proprietà IsBackground sono vere. Ciò significa che un thread ThreadPool non manterrà un'applicazione in esecuzione dopo che tutti i thread in primo piano sono stati interrotti.

L'applicazione termina semplicemente (raggiungendo la fine di Main) prima che i thread finiscano di funzionare.

+1

Un modo per risolvere questo problema è utilizzare Interlocked.Increment per tenere traccia di quante volte viene chiamato DoWork, impostando un evento di reimpostazione automatica quando raggiunge il numero magico. Il thread principale può attendere quell'evento invece di spegnersi. –

+0

Non c'è un modo per dire al thread principale di attendere fino a quando tutti i thread ThreadPool sono stati completati con il lavoro? – shahkalpesh

+1

Il commento di Steven spiega come farlo. –

2

Questa è una versione semplice di ciò a cui alludevo. Usa un singolo evento e non esegue il polling o lo spin, ed è scritto in modo da essere riusabile oltre a consentire più set di lavoro allo stesso tempo. Le espressioni lambda potrebbero essere scomposte, se questo è più conveniente per il debug.

class Program 
{ 
    static void Main(string[] args) 
    { 
     var items = new string[] { "1", "2", "3", "300" }; 
     using (var outfile = File.AppendText("file.txt")) 
     { 
      using (var ws = new WorkSet<string>(x => 
        { lock (outfile) outfile.WriteLine(x); })) 
       foreach (var item in items) 
        ws.Process(item); 
     } 
    } 

    public class WorkSet<T> : IDisposable 
    { 
     #region Interface 

     public WorkSet(Action<T> action) 
     { _action = action; } 

     public void Process(T item) 
     { 
      Interlocked.Increment(ref _workItems); 
      ThreadPool.QueueUserWorkItem(o => 
        { try { _action((T)o); } finally { Done(); } }, item); 
     } 

     #endregion 
     #region Advanced 
     public bool Done() 
     { 
      if (Interlocked.Decrement(ref _workItems) != 0) 
       return false; 

      _finished.Set(); 
      return true; 
     } 

     public ManualResetEvent Finished 
     { get { return _finished; } } 

     #endregion 
     #region IDisposable 

     public void Dispose() 
     { 
      Done(); 
      _finished.WaitOne(); 
     } 

     #endregion 
     #region Fields 

     readonly Action<T> _action; 
     readonly ManualResetEvent _finished = new ManualResetEvent(false); 
     int _workItems = 1; 

     #endregion 
    } 
} 
0

Che ne dici di breve e dolce?

static int wrkThreads = 0; 
    static readonly EventWaitHandle exit = new ManualResetEvent(false); 
    static readonly object syncLock = new object(); 

    static void Main(string[] items) 
    { 
     wrkThreads = items.Length; 

     foreach (var item in items) 
      ThreadPool.QueueUserWorkItem((DoWork), item); 

     exit.WaitOne(); 
    } 

    static void DoWork(object obj) 
    { 
     lock (syncLock) { 
      /* Do your file work here */ 
     } 
     if (Interlocked.Decrement(ref wrkThreads) == 0) 
      exit.Set(); 
    } 
+0

Giusto, questo è l'algoritmo che ho postato, solo estrapolando ogni riusabilità. Richiede anche che tu sappia quanti oggetti di lavoro ci saranno, in anticipo, mentre la soluzione che ho offerto non lo è. Tuttavia, confuta il suggerimento di Spencer che questo algoritmo sia esso stesso lungo. –

+0

Che sia. Motivo che ho postato è che considerando la domanda, l'OP non è ancora grande sul multi-threading e questa versione breve risolve il suo problema dimostrando chiaramente un rudimentale metodo di sincronizzazione dei thread. –

+0

Capisco le tue motivazioni, ma non mi preoccupo del fatto che tu usi un oggetto generico da bloccare al posto dello stesso FileStream. –