2009-10-14 15 views
10

Tengo un objeto que requiere mucha inicialización (1-2 segundos en una máquina fornida). Aunque una vez que se inicializa, solo toma unos 20 milisegundos para realizar un "trabajo" típico¿La forma correcta de tener un hilo de trabajo sin fin?

Para evitar que se reinicialice cada vez que una aplicación quiere usarlo (que podría ser 50 veces por segundo o no) en absoluto durante unos minutos en el uso típico), decidí darle un quehacer de trabajo, y ejecutarlo en su propio hilo, verificando si hay algo de trabajo para él en el que. Sin embargo, no estoy del todo seguro de cómo hacer un hilo que se ejecute de forma indefinida con o sin trabajo.

Esto es lo que tengo hasta ahora, cualquier crítica es bienvenida

private void DoWork() 
    { 
     while (true) 
     { 
      if (JobQue.Count > 0) 
      { 
       // do work on JobQue.Dequeue() 
      } 
      else 
      { 
       System.Threading.Thread.Sleep(50); 
      } 
     } 
    } 

Después de pensamiento: Estaba pensando que podría tener que matar a este hilo con gracia INSEAD de dejar que siga para siempre, así que creo que voy a añadir una Tipo de trabajo que le dice al hilo que debe terminar. También se agradece cualquier idea sobre cómo finalizar un hilo como este.

Respuesta

19

Usted necesidad a lock de todos modos, por lo que puede Wait y Pulse:

while(true) { 
    SomeType item; 
    lock(queue) { 
     while(queue.Count == 0) { 
      Monitor.Wait(queue); // releases lock, waits for a Pulse, 
           // and re-acquires the lock 
     } 
     item = queue.Dequeue(); // we have the lock, and there's data 
    } 
    // process item **outside** of the lock 
} 

con añadir como:

lock(queue) { 
    queue.Enqueue(item); 
    // if the queue was empty, the worker may be waiting - wake it up 
    if(queue.Count == 1) { Monitor.PulseAll(queue); } 
} 

También puede que desee ver en this question, lo que limita el tamaño de la cola (bloqueando si está demasiado lleno).

+0

Cola clásica, bloqueo y pulsación. –

+0

+1 muy bueno ... –

+0

¿Por qué escribir algo que ya se ha escrito? Pfx, bebé. – Will

2

Necesita una primitiva de sincronización, como WaitHandle (mire los métodos estáticos). De esta forma puede 'señalar' al hilo de trabajo que hay trabajo. Comprueba la cola y sigue trabajando hasta que la cola está vacía, momento en el que espera que el mutex lo vuelva a señalar.

hacer uno de los elementos de trabajo sea un mando de dejar de fumar demasiado, por lo que se puede señalar el subproceso de trabajo cuando es el momento para salir del hilo

1

En la mayoría de los casos, lo he hecho bastante similar a cómo' configurar, pero no en el mismo idioma. Tuve la ventaja de trabajar con una estructura de datos (en Python) que bloqueará el hilo hasta que se coloque un elemento en la cola, anulando la necesidad de la llamada de espera.

Si .NET ofrece una clase como esa, buscaría usarla. Un bloqueo de subprocesos es mucho mejor que un subproceso que gira en las llamadas de espera.

El trabajo que puede pasar puede ser tan simple como un "nulo"; si el código recibe un nulo, sabe que es hora de salir del tiempo e irse a casa.

1

Tome el Parallel Framework. Tiene un BlockingCollection<T> que puede usar como cola de trabajos. Cómo lo usaría es:

  1. Cree el BlockingCollection < T> que contendrá sus tareas/trabajos.
  2. crear algunos hilos que tienen un bucle sin fin (while (true) {// obtener trabajo de la cola)
  3. Establecer los hilos que van
  4. Añadir trabajos a la colección cuando vienen disponibles

Los hilos se bloquearán hasta que aparezca un elemento en la colección. Quien sea el turno lo obtendrá (depende de la CPU). Estoy usando esto ahora y funciona muy bien.

También tiene la ventaja de depender de MS para escribir ese bit particularmente desagradable en el que varios subprocesos acceden al mismo recurso. Y cada vez que puedas conseguir a alguien más para que escriba, deberías ir por ello. Suponiendo, por supuesto, que tengan más recursos técnicos/de prueba y experiencia combinada que usted.

+0

¿Quiere decir el CTP marcado como "Este CTP es solo para fines de prueba"? No estoy seguro de que sea una gran recomendación ... en 4.0, está bien, ¡pero eso es beta! –

1

Si realmente no necesita tener la salida del subproceso (y solo quiere que no se mantenga su aplicación en ejecución) puede establecer Thread.IsBackground en true y terminará cuando finalicen todos los subprocesos no de fondo. Will y Marc tienen buenas soluciones para manejar la cola.

1

Implementé una cola de tareas en segundo plano sin utilizar ningún tipo de while bucle, o pulsando, o esperando, o, de hecho, tocando Thread objetos en absoluto. Y parece funcionar. (Me refiero a que ha estado en entornos de producción manejando miles de tareas al día durante los últimos 18 meses sin ningún comportamiento inesperado). Es una clase con dos propiedades significativas, una Queue<Task> y una BackgroundWorker. Hay tres métodos importantes, abreviados aquí:

private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    if (TaskQueue.Count > 0) 
    { 
     TaskQueue[0].Execute(); 
    } 
} 

private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    Task t = TaskQueue[0]; 

    lock (TaskQueue) 
    { 
     TaskQueue.Remove(t); 
    } 
    if (TaskQueue.Count > 0 && !BackgroundWorker.IsBusy) 
    { 
     BackgroundWorker.RunWorkerAsync(); 
    } 
} 

public void Enqueue(Task t) 
{ 
    lock (TaskQueue) 
    { 
     TaskQueue.Add(t); 
    } 
    if (!BackgroundWorker.IsBusy) 
    { 
     BackgroundWorker.RunWorkerAsync(); 
    } 
} 

No es que no haya espera ni pulsos. Pero todo sucede dentro del BackgroundWorker. Esto simplemente se activa cada vez que se tira una tarea en la cola, se ejecuta hasta que la cola está vacía y luego vuelve a dormirse.

Estoy lejos de ser un experto en el roscado. ¿Hay alguna razón para meterse con System.Threading por un problema como este si usa un BackgroundWorker?

+0

En general, estoy de acuerdo con que la clase BackgroundWorker suele ser una opción más simple para administrar una tarea en segundo plano. En este caso, tengo que desafiar algunas de sus reclamaciones.Está utilizando el enhebrado de objetos: la palabra clave 'lock' es azúcar sintáctica para' System.Threading.Monitor.Enter() 'y' System.Threading.Monitor.Exit() '. En lugar de un ciclo while, su controlador de eventos RunWorkerCompleted llama a 'BackgroundWorker.RunWorkerAsync()'. Creo que la lógica de un ciclo wait/pulse while podría ser más fácil de seguir en este caso. –

+0

Bastante justo. Probablemente me parezca más simple porque no tengo experiencia con wait/pulse. –

Cuestiones relacionadas