Я предполагаю, что ваша цель - обработать все данные о шоу, но не более 20 одновременно.Для такого рода задач вам, вероятно, следует использовать ThreadPool
и ограничить максимальное количество одновременных потоков с помощью SetMaxThreads
.
https://docs.microsoft.com/en-us/dotnet/api/system.threading.threadpool?view=netframework-4.7.2
. Необходимо убедиться, что используемая коллекцияхранить ваши результаты является потокобезопасным.
showCast.showCastList = new List<ShowData>();
Я не думаю, что стандартный List является поточно-ориентированным.Потокобезопасная коллекция - ConcurrentBag (есть и другие).Вы можете сделать стандартный список потокобезопасным, но для этого требуется больше кода.После того, как вы закончите обработку и вам понадобятся результаты в списке или массиве, вы можете преобразовать коллекцию в нужный тип.
https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentbag-1?view=netframework-4.7.2
Теперь к использованию семафора.Что делает ваш семафор, так это обеспечивает одновременное создание максимум 20 потоков.Предполагая, что этот цикл выполняется в главном потоке вашего приложения, ваш семафор не имеет смысла.Чтобы заставить его работать, вам нужно освободить семафор после завершения потока;но вы вызываете поток Start()
после вызова Release()
.Это приводит к выполнению потока вне «критической области».
using (Semaphore pool = new Semaphore(20, 20)) {
for (int i = 0; i < allShows.Shows.Length; i++) {
pool.WaitOne();
Thread t = new Thread(new ParameterizedThreadStart((taskId) =>
{
showCast.showCastList.Add(MapResponse(allShows.Shows[i]));
pool.Release();
}));
t.Start(i);
}
}
Я не тестировал это решение;могут возникнуть дополнительные проблемы.
Другая проблема, связанная с этой программой, заключается в том, что она не ожидает завершения всех потоков.Как только все потоки запущены;Программа закончится.Возможно (и в вашем случае я уверен), что не все потоки завершили свою работу;Вот почему ~ 240 пакетов данных сделано, когда программа заканчивается.
thread.Join();
Но при вызове сразу после Start()
он остановит основной поток до его завершения, поэтому для поддержания параллельности программы необходимо создать список потоков и Join()
их в конце программы.Это не лучшее решение.Как ждать во всех потоках, которые программа может добавить к ThreadPool
Подождите, пока все потоки не закончат свою работу в ThreadPool
В качестве последнего примечания вы не можете получить доступ к счетчику циклов, кактот.Окончательное значение счетчика цикла оценивается позже и с тестом, который я запускалкод имеет тенденцию обрабатывать нечетные записи дважды и пропускать четные.Это происходит потому, что цикл увеличивает счетчик перед выполнением предыдущего потока и вызывает доступ к элементам за пределами массива.
Возможное решение - создать метод, создающий поток.Наличие его в отдельном методе оценивает allShows.Shows[i]
до show
перед следующим проходом цикла.
public void CreateAndStartThread(Show show, Semaphore pool, ShowCasts showCast)
{
pool.WaitOne();
Thread t = new Thread(new ParameterizedThreadStart((s) => {
showCast.showCastList.Add(MapResponse((Show)s));
pool.Release();
}));
t.Start(show);
}
Параллельное программирование сложно, и я настоятельно рекомендую выполнить некоторые упражнения с примерами распространенных ошибок.В книгах по программированию на C # наверняка есть глава или две на эту тему.На эту тему можно найти множество онлайн-курсов и учебных пособий.
Редактировать: Рабочее решение.Тем не менее могут возникнуть некоторые проблемы.
public ShowCasts GetAllShowAndTheirCast()
{
ShowResponse allShows = GetAllShows();
ConcurrentBag<ShowData> result = new ConcurrentBag<ShowData>();
using (var countdownEvent = new CountdownEvent(allShows.Shows.Length))
{
using (Semaphore pool = new Semaphore(20, 20))
{
for (int i = 0; i < allShows.Shows.Length; i++)
{
CreateAndStartThread(allShows.Shows[i], pool, result, countdownEvent);
}
countdownEvent.Wait();
}
}
return new ShowCasts() { showCastList = result.ToList() };
}
public void CreateAndStartThread(Show show, Semaphore pool, ConcurrentBag<ShowData> result, CountdownEvent countdownEvent)
{
pool.WaitOne();
Thread t = new Thread(new ParameterizedThreadStart((s) =>
{
result.Add(MapResponse((Show)s));
pool.Release();
countdownEvent.Signal();
}));
t.Start(show);
}