Запуск нескольких потоков, начиная новую, как другое отделку

StackOverflow https://stackoverflow.com/questions/2196945

  •  25-09-2019
  •  | 
  •  

Вопрос

У меня есть приложение, которое имеет много случаев. Каждый случай имеет много умноженных файлов TIF. Мне нужно скрыть файлы TF в файл PDF. Поскольку есть так много файлов, я думал, что смогу построить процесс преобразования. В настоящее время я ограничиваю процесс до десяти преобразования за раз (то есть десять протекторов). Когда одно преобразование завершается, другой должен начать.

Это текущая настройка, которую я использую.

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

Используя это, иногда приводит к исключительному исключительному, указав VATHANTLE.WATALL () или WATHANDLE.WAITANY () параметров массива () не должны превышать длину 65. Что я делаю не так в этом подходе и как я могу исправить это?

Это было полезно?

Решение

Есть несколько проблем с тем, что вы написали.

1-й, это не безопасно в потоке. У вас есть несколько потоков, добавляющих, удаляя и ожидание на массиве AutoResetevents. Отдельные элементы списка могут быть доступны к отдельным потокам, но все, что добавляет, удаляет или проверяет все элементы (например, вызов Waitany), нужно сделать это внутри блокировки.

2-й, нет никакой гарантии, что ваш код будет обрабатывать 10 файлов одновременно. Код между при проверке размера списка, и точка, в которой добавляется новый элемент, открыт для нескольких потоков для просмотра.

3-й, существует потенциал для потоков, запущенных в QueueuserWorkeTem, чтобы преобразовать один и тот же файл. Не скомпируя имя файла внутри цикла, поток, которая преобразует файл, будет использовать любое значение имени файла, когда он выполняет, а не то, что было в именем файла, когда вы называете QueueuserWorkiteTem.

Эта кодовая статья должна указывать вам в правильном направлении для того, что вы пытаетесь сделать: http://www.codeProject.com/kb/threads/schedulingEngine.aspx.

РЕДАКТИРОВАТЬ:

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

Лично я не думаю, что сделал это таким образом ... Но, работая с кодом, который у вас есть, это должно работать.

Другие советы

Вы используете реальный семафор (System.threading.)? При использовании семафоров вы обычно выделяете ваши максимальные ресурсы и автоматически блокируете для вас (при добавлении и выпускании). Вы можете пойти с подходом AntaNy, но я получаю чувство, что вы выбрали более сложный маршрут.

Похоже, вам нужно снять ручку, вызванную функцию Wainany, чтобы продолжить

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

Так что в основном я бы удалил:

semaphores.Remove(semaphore);

Вызвайте из потока и используйте вышеупомянутое, чтобы удалить сигнальное событие и посмотрите, если это работает.

Может быть, вы не должны создавать так много событий?

// 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();
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top