Pergunta

Eu tenho um aplicativo que tem muitos casos.Cada caso tem muitos multipage arquivos tif.Eu preciso converter o tf arquivos para o arquivo pdf.Desde que há assim muitos arquivos, eu pensei que eu poderia thread o processo de conversão.Atualmente estou limitando o processo de dez conversões por um tempo (eu.e dez lagartas).Quando a conversão for concluída, o outro deve começar.

Esta é a configuração atual que estou 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());
  }
}

Com isso, às vezes resulta em uma exceção informando que o WaitHandle.WaitAll() ou WaitHandle.WaitAny() matriz de parâmetros não devem ter um comprimento superior a 65 anos.O que estou fazendo de errado nesta abordagem e como posso corrigi-lo?

Foi útil?

Solução

Existem alguns problemas com o que você escreveu.

1º, não é thread-safe.Você tem vários threads de adicionar, remover e esperando a matriz de AutoResetEvents.Os elementos individuais da Lista pode ser acessado em segmentos separados, mas nada que adiciona, remove ou verificações de todos os elementos (como o WaitAny chamada), precisam fazê-lo dentro de um bloqueio.

2º, não há nenhuma garantia de que seu código vai processar apenas 10 arquivos de uma vez.O código entre o momento em que o tamanho da Lista é verificada, e o ponto em que um novo item é adicionado está aberto para vários segmentos para passar.

3º, há potencial para as threads que começou no QueueUserWorkItem para converter o mesmo arquivo.Sem capturar o nome do arquivo dentro do loop, a thread que converte o arquivo que irá utilizar o valor que está no nome de arquivo quando ele é executado, o que NÃO estava no nome de arquivo quando você chamou QueueUserWorkItem.

Este codeproject artigo deve apontar na direção certa para o que você está tentando fazer: http://www.codeproject.com/KB/threads/SchedulingEngine.aspx

EDITAR:

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);
        }

Pessoalmente, eu não acho que eu iria fazê-lo desta forma...mas, trabalhando com o código que você tem, isso deve funcionar.

Outras dicas

Você está usando um semáforo real (System.Threading)? Ao usar semáforos, você normalmente aloca seus recursos máximos e ele bloqueia automaticamente (conforme você adiciona e libera). Você pode seguir a abordagem de espera, mas estou sentindo que você escolheu o caminho mais difícil.

Parece que você precisa remover a alça do desencadeado, a função de espera para prosseguir

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

Então, basicamente, eu removeria o:

semaphores.Remove(semaphore);

Ligue para o thread e use o acima para remover o evento sinalizado e ver se isso funciona.

Talvez você não deva criar 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top