2010-02-02 8 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(); 
    } 
} 

funciona cinco veces (cada trabajador de BG una vez) y se atasca en el tiempo. ¿Los trabajadores de fondo nunca dejan de estar ocupados? ¿Cómo verifico la disponibilidad?BackgroundWorkers nunca deja de estar ocupado

nota: hay 5 subprocesos de trabajo, esto asegura que ninguno de ellos se detiene, siempre asignándoles trabajo. Pero se niegan a dime cuando están disponibles, pensé que tendría una solución sencilla ..

- [Solicitud editar] ---

En realidad era sólo un parámetro ficticio, lo quité y se olvidó de sacarlo, sólo lo uso para llamar a la DoWork, que hace el trabajo sucio:

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

Y el timeconsumingfunction termina. entrando en el depurador y ejecutando la línea por línea, va hasta el final y llega al final '}'. Eso significa que termina, ¿verdad?

--- [RESPUESTA EDIT] ---- funcionó mediante la sustitución de sólo la línea de

System.Threading.Thread.Sleep(0001); 

con

Application.DoEvents(); 

creo que va a funcionar el fondo, pero no recibe el responder y no actualizar las etiquetas IsBusy.

Gracias a todos, excelentes respuestas, ¡ayudaron mucho!

+0

La manera más fácil de determinar si el trabajo está terminado es colocar un punto de interrupción en el evento RunWorkerCompleted y dejar que el programa se ejecute. Si llega al punto de interrupción, el trabajador de segundo plano ha terminado el trabajo. – 53an

Respuesta

34

Su bucle está causando un punto muerto, el BGW no puede completar. El problema es el evento RunWorkerCompleted, se genera en el hilo de UI. Ese poco de magia de BGW requiere que el hilo de UI esté inactivo, debe estar bombeando su ciclo de mensajes. El problema es que el hilo de UI no está inactivo y no está bombeando mensajes, está atascado en el ciclo for. Por lo tanto, el controlador de eventos no se puede ejecutar e IsBusy se mantiene verdadero.

Tendrá que hacer esto de otra manera. Aproveche el evento RunWorkerCompleted para ejecutar el código que normalmente ejecutaría después de esto para el ciclo. Resista toda tentación de llamar a Application.DoEvents() dentro del ciclo.

+0

Es casi como un eterno, y nada funciona después. Estoy revisando las disponibilidades del sitio web en una matriz (usando el índice de for), por lo que no hay forma de simplemente "continuar" en el RunWorkerCompleted. Si tiene alguna sugerencia, por favor, edite su respuesta .. No tengo ni idea :( – Marcelo

+0

Y HACE completo. Lo he seguido con el depurador, y llega al "}" en un punto dado cuando ha habido algunos duermen (0001) que no es tanto como en tiempo de ejecución, porque hay tiempo entre los pasos, entonces duermo (0001) unas 20 veces ... luego se ejecuta de nuevo, va hasta el '}', pero incluso después el último "}" del doWork, el BGW no cambiará el isBusy a falso. – Marcelo

+3

No, no está completo hasta que el evento RunWorkerCompleted finalizó la ejecución. Lo que sucede después de que el controlador de eventos DoWork haya completado. El controlador de RWC es el problema , no se puede ejecutar porque el hilo de UI está atorado en un bucle. Eliminar el controlador sería una solución rápida. –

0

El .IsBusy solo indica que el backgroundworker está realizando una operación. Estará ocupado hasta que se complete "algo". Parece que "algo" no termina, manteniendo ocupados a los trabajadores de fondo.

Por lo tanto, sería útil si pudiera explicar qué es "algo" y, posiblemente, cuánto tiempo lleva realizar "algo" en el hilo principal.

3

Le sugiero que cambie su código para manejar el evento RunWorkerCompleted para recibir notificaciones cuando su BackgroundWorker s haya terminado con su trabajo. Hay un ejemplo de cómo usar el BackgroundWorker en the official documentation.

2

Tuve el mismo problema al utilizar trabajadores en segundo plano y llegué a la conclusión de que si usa sleep() dentro de un bucle, se quedará atascado. Puede utilizar el evento RunWorkerCompleted y establecer un indicador booleano para indicar cuándo finaliza cada trabajador.

O si desea abortar el hilo, independientemente de que pueda ver el uso de hilos en lugar de trabajadores de fondo. Sin embargo, luego pierde la facilidad de uso en lo que respecta a los eventos que proporciona el trabajador de fondo.

+0

Urrgh - sí, no seas perezoso - sleep() solo me mordió demasiado –

1

Su hilo principal debe ser bombeo de mensajes de Windows (ya sea llamando a Application.DoEvents en su ciclo while, o mejor aún utilizando un Systems.Windows.Forms.Timer en lugar de un bucle).

Si no bombea los mensajes de Windows, las notificaciones "completadas" de su trabajador en segundo plano no se procesarán, por lo que el estado permanecerá ocupado.

0

El problema es que todo lo que esté haciendo dentro de worker.RunWorkerAsync() nunca se termina. Tal vez haya algún ciclo infinito o algo así definido en su evento DoWork.

Aquí está un ejemplo de trabajo, que se selecciona el siguiente trabajador libre:

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

tuve un problema similar, y lo que hice para resolver era para encerrar la función principal con una declaración try... catch... and finally....

-1

En el siguiente ejemplo se proporciona una solución de trabajo para su problema. Al igual que Hans Passant explicó que su código se ejecuta en backgroundworker pero RunTorkerCompleted de cada hilo solo se evalúa en el subproceso ui. Para hacer esto, puede enrutar su solicitud en la cola de hilos de UI, ThreadPool. De esta forma, la IU evalúa RunWorkerCompletedEvent y luego regresa a tu código.

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

Downvoting sin dar la razón no es útil ni agradable. Esta respuesta es válida para la pregunta formulada y muestra cómo salir del punto muerto utilizando el mayor número posible de códigos del interrogador. – codingdave

Cuestiones relacionadas