Запуск нескольких потоков, начиная новую, как другое отделку
-
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();