Лучший способ справиться с этим - создать только maxDownloads
количество потоков. Поместите все свои рабочие элементы в очередь и позвольте потокам конкурировать друг с другом, чтобы выяснить, какой из них обрабатывает каждый рабочий элемент.
var queue = new ConcurrentQueue<downloadItem>(downloadList);
for (int i = 0; i < Math.Min(maxDownloads, queue.Count))
{
var thread = new Thread(
() =>
{
while (true)
{
downloadItem item = null;
if (queue.TryDequeue(out item))
{
// Process the next work item.
DownloadItem(item);
}
else
{
// No more work items are left.
break;
}
}
});
thread.IsBackground = true;
thread.Start();
}
Вы также можете использовать семафор для регулирования количества потоков, обрабатывающих рабочие элементы. Это особенно полезно, когда фактическое количество потоков неизвестно, как в случае, если вы использовали ThreadPool
.
var semaphore = new Semaphore(maxDownloads, maxDownloads);
for (int i = 0; i < downloadList.Count; i++)
{
downloadItem item = downloadList[i];
ThreadPool.QueueUserWorkItem(
(state) =>
{
semaphore.WaitOne();
try
{
DownloadItem(item);
}
finally
{
semaphore.Release();
}
});
}
Я не особенно люблю оба подхода. Проблема с первым заключается в том, что создается нефиксированное количество потоков. Как правило, рекомендуется избегать создания потоков в цикле for
, поскольку это имеет тенденцию плохо масштабироваться. Проблема со вторым состоит в том, что семафор заблокирует некоторые из потоков ThreadPool
. Это также не рекомендуется, потому что вы эффективно запрашиваете один из потоков, а затем ничего не делаете с ним. Это может повлиять на выполнение других не связанных задач, которые совместно используют ThreadPool
. Я думаю, что в этом случае любой из двух вариантов подойдет, поскольку создание более масштабируемого шаблона - это больше работы, чем оно того стоит.