Pregunta

Tengo una aplicación que tiene muchos casos. Cada caso tiene muchos archivos tif de varias páginas. Necesito para convertir los archivos de TF a archivo PDF. Puesto que hay tantos archivos, pensé que podía enhebrar el proceso de conversión. Actualmente estoy limitando el proceso a diez conversiones a la vez (es decir, bandas de rodadura de diez). Cuando una completa conversión, debe comenzar otra.

Esta es la configuración actual que estoy usando.

private void ConvertFiles()
{
  List<AutoResetEvent> semaphores = new List<AutoResetEvet>();
  foreach(String fileName in filesToConvert)
  {
    String file = fileName;

    if(semaphores.Count >= 10)
    {
      WaitHandle.WaitAny(semaphores.ToArray());
    }


    AutoResetEvent semaphore = new AutoResetEvent(false);
    semaphores.Add(semaphore);

    ThreadPool.QueueUserWorkItem(
      delegate
      { 
        Convert(file);
        semaphore.Set();
        semaphores.Remove(semaphore);
      }, null);
  }

  if(semaphores.Count > 0)
  {
    WaitHandle.WaitAll(semaphores.ToArray());
  }
}

El uso de este, a veces resulta en una excepción que indica la WaitHandle.WaitAll () o WaitHandle.WaitAny () parámetros de matriz no deben exceder una longitud de 65. ¿Qué estoy haciendo mal en este enfoque y cómo puedo corregirlo?

¿Fue útil?

Solución

Hay algunos problemas con lo que ha escrito.

primero, no es seguro para subprocesos. Tiene varios hilos Instalar, desinstalar y esperando en la gama de AutoResetEvents. Los elementos individuales de la lista se puede acceder en hilos separados, pero cualquier cosa que añade, elimina o cheques todos los elementos (como la llamada WaitAny), necesidad de hacerlo en el interior de una cerradura.

segundo, no hay ninguna garantía de que su código sólo procesará 10 archivos a la vez. El código entre cuando se comprueba el tamaño de la lista, y el punto donde se añade un elemento nuevo está abierto para varios subprocesos para pasar.

tercero, existe el potencial de las discusiones iniciadas en el QueueUserWorkItem para convertir el mismo archivo. Sin capturar el nombre del archivo dentro del bucle, el hilo que convierte el archivo a utilizar lo que es el valor de nombre de archivo al que se ejecuta, no lo era en el nombre de archivo al que llamó QueueUserWorkItem.

Este artículo CodeProject debe apuntar en la dirección correcta para lo que está tratando de hacer: http://www.codeproject.com/KB/threads/SchedulingEngine.aspx

EDIT:

var semaphores = new List<AutoResetEvent>();
        foreach (String fileName in filesToConvert)
        {
            String file = fileName;
            AutoResetEvent[] array;
            lock (semaphores)
            {
                array = semaphores.ToArray();
            }
            if (array.Count() >= 10)
            {
                WaitHandle.WaitAny(array);
            }

            var semaphore = new AutoResetEvent(false);
            lock (semaphores)
            {
                semaphores.Add(semaphore);
            }
            ThreadPool.QueueUserWorkItem(
              delegate
              {
                  Convert(file);
                  lock (semaphores)
                  {
                      semaphores.Remove(semaphore);
                  }
                  semaphore.Set();
              }, null);
        }

En lo personal, yo no creo que lo haría de esta manera ... pero, trabajando con el código que tiene, esto debería funcionar.

Otros consejos

¿Está utilizando un semáforo real ( System.Threading )? Cuando se utilizan los semáforos, por lo general asignar sus recursos máximos y se bloqueará de forma automática (a medida que agrega y liberación). Usted puede ir con el enfoque WaitAny, pero estoy teniendo la sensación de que ha elegido el camino más difícil.

Parece que necesitas para retirar el mango del desencadenó la función WaitAny proceder

if(semaphores.Count >= 10)
{
  int index = WaitHandle.WaitAny(semaphores.ToArray());
  semaphores.RemoveAt(index);
}

Así que básicamente me quite el:

semaphores.Remove(semaphore);

llamada del hilo y utilizar lo anterior para eliminar el evento señalado y ver si funciona.

Tal vez no deberías crear tantos eventos?

// input
var filesToConvert = new List<string>();
Action<string> Convert = Console.WriteLine;

// limit
const int MaxThreadsCount = 10;

var fileConverted = new AutoResetEvent(false);
long threadsCount = 0;

// start
foreach (var file in filesToConvert) {
    if (threadsCount++ > MaxThreadsCount) // reached max threads count 
        fileConverted.WaitOne();          // wait for one of started threads

    Interlocked.Increment(ref threadsCount);

    ThreadPool.QueueUserWorkItem(
        delegate {
            Convert(file);

            Interlocked.Decrement(ref threadsCount);
            fileConverted.Set();
        });
}

// wait
while (Interlocked.Read(ref threadsCount) > 0) // paranoia?
    fileConverted.WaitOne();
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top