Отладка RtlUserThreadStart в Process Explorer

У меня есть многопоточное приложение wpf, построенное на 3.5. Когда я смотрю на запущенные потоки через Process Explorer, я вижу 8 потоков, все с одинаковым начальным адресом, ntdll.dll!RtlUserThreadStart, и все восемь имеют значение ЦП от 3-6+ и имеют высокую дельту циклов. Я не могу понять, что делают эти темы. Это всегда одни и те же темы. Он никогда не меняется в пределах одного и того же экземпляра приложения. Когда я отлаживаю свое приложение одновременно и приостанавливаю отладчик, все эти потоки показывают одну строку для стека либо System.Threading.ConcurrencyScheduler.Scheduler.WaitForWork(), либо System.Threading.Monitor.Wait().

Я включил файлы символов для Visual Studio и вижу следующий стек в этих потоках:

System.Threading.Monitor.Wait() Normal
mscorlib.dll!System.Threading.Monitor.Wait(object obj, int millisecondsTimeout) + 0x19     bytes
System.Threading.dll!System.Threading.ConcurrencyScheduler.Scheduler.WaitForWork() + 0xd0 bytes  
System.Threading.dll!System.Threading.ConcurrencyScheduler.InternalContext.Dispatch() + 0x74a bytes
System.Threading.dll!System.Threading.ConcurrencyScheduler.ThreadInternalContext.ThreadStartBridge(System.IntPtr dummy) + 0x9f bytes     

Когда я смотрю на стек, предоставленный потоком в мониторе процесса, я вижу следующее в качестве примеров:

0  ntoskrnl.exe!KeWaitForMultipleObjects+0xc0a
1  ntoskrnl.exe!KeAcquireSpinLockAtDpcLevel+0x732
2  ntoskrnl.exe!KeWaitForSingleObject+0x19f
3  ntoskrnl.exe!_misaligned_access+0xba4
4  ntoskrnl.exe!_misaligned_access+0x1821
5  ntoskrnl.exe!_misaligned_access+0x1a97
6  mscorwks.dll!InitializeFusion+0x990b
7  mscorwks.dll!DeleteShadowCache+0x31ef

или же:

0  ntoskrnl.exe!KeWaitForMultipleObjects+0xc0a
1  ntoskrnl.exe!KeAcquireSpinLockAtDpcLevel+0x732
2  ntoskrnl.exe!KeWaitForSingleObject+0x19f
3  ntoskrnl.exe!_misaligned_access+0xba4
4  ntoskrnl.exe!_misaligned_access+0x1821
5  ntoskrnl.exe!KeAcquireSpinLockAtDpcLevel+0x93d
6  ntoskrnl.exe!KeWaitForMultipleObjects+0x26a
7  ntoskrnl.exe!NtWaitForSingleObject+0x41f
8  ntoskrnl.exe!NtWaitForSingleObject+0x78e
9  ntoskrnl.exe!KeSynchronizeExecution+0x3a23
10 ntdll.dll!ZwWaitForMultipleObjects+0xa
11 KERNELBASE.dll!GetCurrentProcess+0x40
12 KERNEL32.dll!WaitForMultipleObjectsEx+0xb3
13 mscorwks.dll!CreateApplicationContext+0x10499
14 mscorwks.dll!CreateApplicationContext+0xbc41
15 mscorwks.dll!StrongNameFreeBuffer+0xc54d
16 mscorwks.dll!StrongNameFreeBuffer+0x2ac48
17 mscorwks.dll!StrongNameTokenFromPublicKey+0x1a5ea
18 mscorwks.dll!CopyPDBs+0x17362
19 mscorwks.dll!CorExitProcess+0x3dc9
20 mscorwks.dll!TranslateSecurityAttributes+0x547f
21 mscorlib.ni.dll+0x8e6bc9

В качестве дополнительного примечания к этому пункту. Мой компьютер представляет собой один процессор с 4 ядрами. Когда мы запускаем одно и то же приложение на двухъядерном процессоре с четырьмя ядрами, мы видим, что число потоков увеличивается с 8 до 16.

2 ответа

Ваш вопрос ужасно недокументирован, но разумно предположить, что вы, похоже, используете библиотеку PPL. Который держит пул потоков вокруг, чтобы выполнить параллельные работы. Вы, несомненно, видите большое количество циклов ЦП, потому что эти потоки действительно выполняют ту работу, которую вы просили их выполнить.

Как обычно для пулов потоков, PPL хранит эти потоки для следующей работы, поэтому вы ожидаете их в WaitForWork(). Собственные трассировки стека являются ненужными из-за отсутствия символов отладки. В противном случае RtlUserThreadStart - это функция Windows, которую вы всегда будете видеть в трассировке неуправляемого стека, именно так запускается поток.

Это все совершенно нормально. Единственная информация, заслуживающая внимания, - это ответ сотрудника Microsoft:

Среда выполнения параллелизма кэширует потоки для последующего повторного использования. Они выпускаются только тогда, когда все планировщики времени выполнения параллелизма были выключены. (Как правило, в процессе есть только один планировщик по умолчанию). Планировщик отключается, когда все внешние потоки, находящиеся в очереди, работают с ним. Таким образом, если основной поток запланировал работу (вызвав параллель_for из main(), скажем), то планировщик по умолчанию будет удален только при завершении процесса.

Существует верхний предел количества кэшируемых потоков. Это примерно в 4 раза больше количества ядер на машине (хотя существуют некоторые другие факторы, влияющие на порог, например, размер стека в политиках планировщика).

Я выяснил, что вызывает высокую загрузку ЦП в этих потоках, которые находятся в состоянии ожидания. Я пока не знаю, почему это происходит. Когда наше приложение было приложением.NET 3.5, кто-то здесь нашел и использовал доступную сборку потоков, которую кто-то перенес обратно или что-то из.NET 4.0/4.5 для использования с 3.5. Это, очевидно, имеет дефект в вызове Parallel.ForEach или что-то. Когда я вызываю этот вызов, я получаю эти потоки, которые бездействуют, ожидая после цикла, потребляя процессор. Мы подтвердили в Microsoft, что эти темы на самом деле просто ждут. Сейчас у нас 4.0, и я переключился на библиотеку задач, доступную с 4.0, и проблема исчезла. Я попытаюсь отладить библиотеку, когда у меня будет возможность проверить, могу ли я указать конкретную причину, по которой это происходит.

Другие вопросы по тегам