Хорошо ... Я провел тщательный поиск по сайту и прочитал много сообщений по этой теме. Я нашел этот вопрос: Код для простого пула потоков на C# особенно полезным.
Однако, как всегда кажется, то, что мне нужно, немного варьируется.
Я просмотрел пример MSDN и несколько адаптировал его под свои нужды. Пример, на который я ссылаюсь, находится здесь: http://msdn.microsoft.com/en-us/library/3dasc8as(VS.80,printer).aspx
Моя проблема вот в чем. У меня есть довольно простой набор кода, который загружает веб-страницу через классы HttpWebRequest и WebResponse и считывает результаты через Stream. Я запускаю этот метод в потоке, так как он должен выполняться много раз. Сам метод довольно короткий, но количество его запусков (с разными данными для каждого раза) варьируется. Это может быть от 1 до 200.
Все, что я прочитал, похоже, указывает на то, что класс ThreadPool является главным кандидатом. Вот что бывает сложно. Мне может потребоваться запустить эту штуку, скажем, 100 раз, но у меня может быть не более 3 запущенных потоков (для этой конкретной задачи).
Я пробовал установить MaxThreads на ThreadPool через:
ThreadPool.SetMaxThreads(3, 3);
Я не совсем уверен, что этот подход работает. Более того, я не хочу затирать другие веб-сайты или программы, работающие в системе, в которой они будут работать. Итак, ограничивая количество потоков на ThreadPool, могу ли я быть уверен, что это относится только к моему коду и моим потокам?
В примере MSDN используется подход событийного диска и вызывается WaitHandle.WaitAll(doneEvents);, как я это делаю.
Итак, суть моего вопроса заключается в том, как обеспечить или указать максимальное количество потоков, которые могут быть запущены для их кода, но чтобы код продолжал выполнять больше потоков по мере того, как предыдущие завершались до некоторой произвольной точки? Правильно ли я подхожу к этому?
Искренне,
Джейсон
Хорошо, я добавил семафорный подход и полностью удалил код ThreadPool. Это кажется достаточно простым. Я получил информацию от: http://www.albahari.com/threading/part2.aspx
Этот пример показал мне, как:
[текст ниже является копией / вставкой с сайта]
Semaphore с емкостью, равной единице, аналогичен Mutex или lock, за исключением того, что у Semaphore нет «владельца» - он не зависит от потоков. Любой поток может вызвать Release на Semaphore, тогда как с Mutex и lock только поток, получивший ресурс, может освободить его.
В следующем примере десять потоков выполняют цикл с оператором Sleep посередине. Semaphore гарантирует, что не более трех потоков могут выполнять этот оператор Sleep одновременно:
class SemaphoreTest
{
static Semaphore s = new Semaphore(3, 3); // Available=3; Capacity=3
static void Main()
{
for (int i = 0; i < 10; i++)
new Thread(Go).Start();
}
static void Go()
{
while (true)
{
s.WaitOne();
Thread.Sleep(100); // Only 3 threads can get here at once
s.Release();
}
}
}





Это статический класс, как и любой другой, а это означает, что все, что вы с ним делаете, влияет на все остальные потоки текущего процесса. Это не влияет на другие процессы.
Однако я считаю это одним из самых серьезных недостатков дизайна .NET. Кому пришла в голову блестящая идея сделать пул потоков статический? Как показывает ваш пример, нам часто нужен пул потоков, выделенный для задачи наш, чтобы он не мешал несвязанным задачам в другом месте системы.
Это не значит, что это не недостаток дизайна. Им следовало разрешить создание его экземпляров по запросу с первого дня. Конечно, ничто не мешает им также предоставить статический экземпляр этого класса, если они хотят явно поддерживать «общепроцессные» задачи. Но я рад, что .NET4 наконец исправит это.
Это имеет большой смысл, когда вы понимаете, что количество доступных процессоров / ядер также довольно статично. И они в любом случае используются всеми другими процессами, задачами и потоками. Так какой же смысл в том, чтобы два пула потоков в одном приложении конкурировали друг с другом за один и тот же набор ресурсов ЦП?
Контроль. Допустим, у вас есть четыре ядра и две группы задач, которые никогда не должны полностью блокировать друг друга. Затем вы можете создать два пула потоков по три потока в каждом. У меня вопрос, почему нет это разрешает. Какой смысл в явном предотвращении этого?
Вы можете легко создать свой собственный пул потоков, если не хотите использовать статический.
@Judah - я считаю, что параллельная библиотека по умолчанию использует ThreadPool под покровом, поэтому, если статический ThreadPool представляет собой проблему без библиотеки Parallel, это все равно будет проблемой с участием, параллельная библиотека.
Примечание: если вы ограничиваете это число до «3», чтобы не перегружать компьютер, на котором запущено ваше приложение, я бы сначала убедился, что это проблема. Пул потоков должен управлять этим за вас. С другой стороны, если вы не хотите перегружать какой-либо другой ресурс, читайте дальше!
Вы не можете управлять размером пула потоков (или вообще чем-либо еще).
В этом случае я бы использовал семафор для управления доступом к вашему ресурсу. В вашем случае ваш ресурс выполняет парсинг веб-страниц или вычисляет какой-то отчет и т. д.
Для этого в своем статическом классе создайте объект семафора:
System.Threading.Semaphore S = new System.Threading.Semaphore(3, 3);
Затем в каждом потоке вы делаете следующее:
System.Threading.Semaphore S = new System.Threading.Semaphore(3, 3);
try
{
// wait your turn (decrement)
S.WaitOne();
// do your thing
}
finally {
// release so others can go (increment)
S.Release();
}
Каждый поток будет блокироваться на S.WaitOne () до тех пор, пока ему не будет дан сигнал о продолжении. Как только S будет уменьшено 3 раза, все потоки будут блокироваться, пока один из них не увеличит счетчик.
Это решение не идеально.
Если вам нужно что-то более чистое и эффективное, я бы рекомендовал использовать подход BlockingQueue, при котором вы помещаете работу, которую хотите выполнить, в глобальный объект Blocking Queue.
Между тем, у вас есть три потока (которые вы создали - не в пуле потоков), которые выводят работу из очереди для выполнения. Это не так сложно настроить, это очень быстро и просто.
Примеры:
Разве это не должно быть Semaphore (3,3), уменьшение WaitOne и приращение Release?
@Bjarke Ebert: спасибо за исправление: я обновил ответ
Майкл, не могли бы вы подробнее рассказать о примере с семафором? Я впервые прочитал / услышал о них.
Джейсон: Я определенно рекомендую второй подход (блокирующая очередь / производитель-потребитель) - это намного проще, IMO. Тем не менее, чтобы узнать больше о семафорах, прочтите это: msdn.microsoft.com/en-us/library/…
Пул потоков предназначен для использования в рамках всего процесса. Вам нужен диспетчер задач, который можно установить по запросу. Если вы посмотрите на ParallelLINQ и библиотеку параллельных задач от Microsoft (которая будет добавлена в .NET 4), то увидите, что вы ищете.