Я использую ThreadPool.QueueUserWorkItem() для запуска некоторых фоновых задач.
Поведение параллелизма ThreadPool странно. Мой процессор имеет 4 логических ядра. Я ожидаю, что есть только 4 запущенных потока. Однако пример кода показывает другое поведение.
вот пример кода:
class Program
{
static DateTime s_startTime = new DateTime();
public static void Main()
{
// Queue the task.
s_startTime = DateTime.Now;
for (int i = 0; i < 1000; i++)
{
ThreadPool.QueueUserWorkItem(ThreadProc, i);
}
Console.WriteLine("Main thread does some work, then sleeps.");
Thread.Sleep(100 * 1000);
Console.WriteLine("Main thread exits.");
}
// This thread procedure performs the task.
static void ThreadProc(Object i)
{
DateTime thread_starttime = DateTime.Now;
int a = Convert.ToInt32(i);
double ss = (thread_starttime - s_startTime).TotalSeconds;
Console.WriteLine("time:" + ss + ", start " + a);
Thread.Sleep(10 * 1000);
DateTime e = DateTime.Now;
double ee = (e - s_startTime).TotalSeconds;
Console.WriteLine("time:" + ee + ", end " + a);
}
}
выход
Main thread does some work, then sleeps.
time:0.0040027, start 0
time:0.0360007, start 3
time:0.0360007, start 1
time:0.0360007, start 2
time:1.0178537, start 4
time:2.0191713, start 5
time:3.019311, start 6
time:4.0194503, start 7
time:5.0195775, start 8
time:6.0195875, start 9
time:7.0219127, start 10
time:8.0214611, start 11
time:9.0181507, start 12
time:10.020686, end 0
time:10.020686, start 13
time:10.020686, start 14
time:10.038517, end 1
time:10.038517, start 15
time:10.038517, end 3
time:10.0403473, start 16
time:10.038517, end 2
time:10.0413736, start 17
time:11.0233302, end 4
time:11.0243333, start 18
time:11.0243333, start 19
Вы должны использовать StopWatch для проведения бенчмаркинга вместо DateTime.Now.
ThreadPool.GetMaxThreads() показывает рабочие потоки = 1023 по умолчанию. После Set ThreadPool.SetMaxThreads(4, 4) поведение ожидается.
Обратите внимание: если у вас есть реальное приложение и вам действительно нужно убедиться, что обработано столько элементов, сколько у вас ядер (например, потому что вся ваша работа выполняется долго), вы можете использовать Parallel.For[Each] с пользовательским MaxDegreeOfParallelism. Настройка параметров глобального пула потоков хороша для демонстрационных целей, но обычно это не то, что вы хотите делать в рабочем приложении, за исключением обхода неоптимального дизайна.
@JeroenMostert, я согласен с тобой. Parallel.ForEach приветствуется. Я разместил этот пример кода, потому что я отлаживаю чужой код и обнаружил это связанное поведение.





Будет запущено больше потоков, потому что вы не держите свои ядра занятыми.
Нить.Сон позволяет потоку уступать. Поскольку ваши рабочие процессы в основном спят и не привязаны к процессору, планировщик может планировать больше потоков.
Означает ли это, что система не будет запускать больше потоков, если ThreadProc() очень занят (вычисления, связанные с вводом-выводом/процессором)?
Это действительно зависит от занятости как и настроек пула. Цель планировщика — максимально использовать ЦП.
Пул потоков не является статическим. Больше потоков будет развернуто и задействовано в работе, если обнаружится, что текущие потоки находятся в спящем режиме (менее поэтично, если они спят, не выполняя полезной работы). В противном случае не было бы особого смысла в пуле потоков и размещении элементов в очереди — вы всегда могли бы просто раскрутить
Environment.ProcessorCountпотоки самостоятельно и покончить с этим.