2010-02-02 5 views
9
for (do it a bunch of times) 
{   
    while (backgroundWorker1.IsBusy && backgroundWorker2.IsBusy && 
      backgroundWorker3.IsBusy && backgroundWorker4.IsBusy && 
      backgroundWorker5.IsBusy) 
    { 
     System.Threading.Thread.Sleep(0001); 
    } 

    if (!backgroundWorker1.IsBusy) 
    { 
     backgroundWorker1.RunWorkerAsync(); 
    } 
    else if (!backgroundWorker2.IsBusy) 
    { 
     backgroundWorker2.RunWorkerAsync(); 
    } 
    else if (!backgroundWorker3.IsBusy) 
    { 
     backgroundWorker3.RunWorkerAsync(); 
    } 
    else if (!backgroundWorker4.IsBusy) 
    { 
     backgroundWorker4.RunWorkerAsync(); 
    } 
    else if (!backgroundWorker5.IsBusy) 
    { 
     backgroundWorker5.RunWorkerAsync(); 
    } 
} 

esegue cinque volte (ogni lavoratore BG una volta) e rimane bloccato nel frattempo. I lavoratori del background non smettono mai di essere occupati? come controllo la disponibilità?BackgroundI lavoratori non smettono mai di essere occupati

nota: ci sono 5 thread di lavoro, questo assicura che nessuno di loro è mai stato fermato, assegnando sempre il lavoro a loro. Ma si rifiutano di dirmi quando sono disponibili, ho pensato che avrebbe una soluzione semplice ..

- [Richiesta modifica] ---

In realtà era solo un parametro fittizio, ho rimosso e dimenticato di farlo uscire, io uso solo per chiamare il DoWork, che fa il lavoro sporco:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    timeconsumingfunction(publicstring); 
} 

E la timeconsumingfunction finisce. entrando nel debugger e eseguendo la linea per riga, va fino alla fine e arriva nel finale '}'. Ciò significa che finisce, giusto?

--- [RISPOSTA EDIT] ---- ha funzionato, semplicemente sostituendo la linea

System.Threading.Thread.Sleep(0001); 

con

Application.DoEvents(); 

Credo che correrebbe il fondo, ma non riceve il rispondi e non aggiornare i tag IsBusy.

Grazie a tutti, ottime risposte, ha aiutato molto!

+0

Il modo più semplice per determinare se il processo è terminato consiste nel rilasciare un punto di interruzione sull'evento RunWorkerCompleted e consentire l'esecuzione del programma. Se si preme il punto di interruzione, l'operatore in background ha terminato il lavoro. – 53an

risposta

34

Il loop sta causando un deadlock, i BGW non possono essere completati. Il problema è l'evento RunWorkerCompleted, generato nel thread dell'interfaccia utente. Quel pezzetto di magia BGW richiede che il thread dell'interfaccia utente sia inattivo, deve pompare il suo loop di messaggi. Il problema è che il thread UI non è inattivo e non sta pompando messaggi, è bloccato nel ciclo for. Pertanto, il gestore eventi non può essere eseguito e IsBusy rimane true.

Avrete bisogno di farlo in modo diverso. Sfrutta l'evento RunWorkerCompleted per eseguire il codice normalmente eseguito dopo questo ciclo for. Resiste a tutte le tentazioni a chiamare Application.DoEvents() all'interno del ciclo.

+0

È quasi come un eterno per, e niente lo rincorre. Sto controllando le disponibilità del sito Web in un array (usando l'indice di for), quindi non c'è modo di "continuare" in RunWorkerCompleted. Se hai qualche suggerimento, per favore, modifica la tua risposta .. Io sono senza tracce :( – Marcelo

+0

E COMPLETO. L'ho seguito con il debugger, e arriva a "}" ad un certo punto quando c'è stato un po 'di sonno (0001) che non è tanto come in runtime, perché c'è tempo tra i passi, quindi dormo (0001) circa 20 volte ... poi ricomincia, va fino a'} ', ma anche dopo l'ultimo "}" di doWork, BGW non cambierà isBusy in falso – Marcelo

+3

No, non è completo fino a quando l'evento RunWorkerCompleted non ha terminato l'esecuzione. Cosa succede dopo il completamento del gestore di eventi DoWork. Il gestore RWC è il problema , non può essere eseguito perché il thread dell'interfaccia utente è bloccato in un ciclo. Rimuovere il gestore sarebbe una soluzione rapida. –

0

Il .IsBusy indica solo che il backgroundworker sta effettivamente eseguendo un'operazione. Sarà occupato finché "qualcosa" non sarà completato. Sembra che "qualcosa" non finisca, tenendo occupati i tuoi precedenti.

Quindi sarebbe di grande aiuto se potessi spiegare cos'è "qualcosa", e possibilmente quanto tempo ci vuole per eseguire "qualcosa" sul thread principale.

3

Ti suggerisco di modificare il codice per gestire l'evento RunWorkerCompleted per ottenere notifiche quando i tuoi BackgroundWorker s hanno finito con il loro lavoro. C'è un esempio di come utilizzare lo BackgroundWorker in the official documentation.

2

Ho avuto lo stesso problema durante l'utilizzo di operatori in background e sono giunto alla conclusione che se si utilizza sleep() all'interno di un ciclo, si bloccherà. È possibile utilizzare l'evento RunWorkerCompleted e impostare un flag booleano per indicare quando ciascun worker ha terminato.

Oppure se si desidera interrompere il thread indipendentemente da come si può utilizzare utilizzando thread anziché worker in background. Tuttavia, si perde la facilità d'uso in relazione agli eventi forniti dal lavoratore in background.

+0

Urrgh - sì, non essere pigro - dormire() solo un po 'me stesso –

1

Il thread principale deve pompare messaggi di Windows (chiamando Application.DoEvents nel tuo ciclo while, o meglio ancora usando Systems.Windows.Forms.Timer invece di un ciclo).

Se non si pompano messaggi di Windows, le notifiche "completate" del personale in background non verranno elaborate, quindi lo stato resterà occupato.

0

Il problema è che qualunque cosa tu stia facendo entro worker.RunWorkerAsync() non viene mai completata. Forse c'è un ciclo infinito o qualcosa di simile definito nel tuo evento DoWork.

Ecco un esempio di lavoro, che seleziona il prossimo lavoratore libero:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Threading; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     private static List<MyWorker> _Workers; 

     static void Main(string[] args) 
     { 
      _Workers = new List<MyWorker>(); 

      for (int i = 0; i < 5; i++) 
      { 
       _Workers.Add(CreateDefaultWorker(i)); 
      } 

      StartJobs(20000); 
      Console.ReadKey(); 
     } 

     private static void StartJobs(int runtime) 
     { 
      Random rand = new Random(); 
      DateTime startTime = DateTime.Now; 

      while (DateTime.Now - startTime < TimeSpan.FromMilliseconds(runtime)) 
      { 
       var freeWorker = GetFreeWorker(); 

       if (freeWorker != null) 
       { 
        freeWorker.Worker.RunWorkerAsync(new Action(() => DoSomething(freeWorker.Index, rand.Next(500, 2000)))); 
       } 
       else 
       { 
        Console.WriteLine("No free worker available!"); 
        Console.WriteLine("Waiting for free one..."); 
        WaitForFreeOne(); 
       } 
      } 
     } 

     private static MyWorker GetFreeWorker() 
     { 
      foreach (var worker in _Workers) 
      { 
       if (!worker.Worker.IsBusy) 
        return worker; 
      } 

      return null; 
     } 

     private static void WaitForFreeOne() 
     { 
      while (true) 
      { 
       foreach (var worker in _Workers) 
       { 
        if (!worker.Worker.IsBusy) 
         return; 
       } 
       Thread.Sleep(1); 
      } 
     } 

     private static MyWorker CreateDefaultWorker(int index) 
     { 
      var worker = new MyWorker(index); 

      worker.Worker.DoWork += (sender, e) => ((Action)e.Argument).Invoke(); 
      worker.Worker.RunWorkerCompleted += (sender, e) => Console.WriteLine("Job finished in worker " + worker.Index); 

      return worker; 
     } 

     static void DoSomething(int index, int timeout) 
     { 
      Console.WriteLine("Worker {1} starts to work for {0} ms", timeout, index); 
      Thread.Sleep(timeout); 
     } 
    } 

    public class MyWorker 
    { 
     public int Index { get; private set; } 
     public BackgroundWorker Worker { get; private set; } 

     public MyWorker(int index) 
     { 
      Index = index; 
      Worker = new BackgroundWorker(); 
     } 
    } 
} 
1

ho avuto un problema simile, e quello che ho fatto per risolverlo è stato quello di racchiudere la funzione principale con una dichiarazione try... catch... and finally....

-1

Nell'esempio seguente viene fornita una soluzione di lavoro per il tuo problema. Come Hans Passant ha spiegato che il codice viene eseguito nel backgroundworker, ma RunWorkerCompleted di ogni thread viene valutato solo nel thread ui. Per fare ciò è possibile accodare la richiesta nella coda di thread UI, il ThreadPool. In questo modo l'interfaccia utente valuta RunWorkerCompletedEvent e poi torna al tuo codice.

for (int i = 0; i < 999999; ++i) 
{ 
    System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback((x) => 
    { 
     while (backgroundWorker1.IsBusy && backgroundWorker2.IsBusy) 
     { 
      System.Threading.Thread.Sleep(0); 
     } 
     // Code that is beging executed after all threads have ended. 
     myCode(); 
    })); 

    if (!backgroundWorker1.IsBusy) 
    { 
     backgroundWorker1.RunWorkerAsync(); 
    } 
    else if (!backgroundWorker2.IsBusy) 
    { 
     backgroundWorker2.RunWorkerAsync(); 
    }   
} 
+0

Il downvoting senza dare la motivazione non è né utile né piacevole. Questa risposta è una risposta valida alla domanda posta e mostra come uscire dall'impasse usando il maggior numero possibile di codice dall'interrogatore. – codingdave

Problemi correlati