2015-04-22 5 views
4

С помощью этого кода:Thread заблокирован после ждать

static void Main(string[] args) 
    { 
     Console.WriteLine("Main Thread Pre - " + GetNativeThreadId(System.Threading.Thread.CurrentThread)); 
     Task.Run(() => AsyncMethod()).Wait(); 
     Console.WriteLine("Main Thread Post - " + GetNativeThreadId(System.Threading.Thread.CurrentThread)); 
     Console.ReadKey(); 
    } 

    static async Task AsyncMethod() 
    { 
     Console.WriteLine("AsyncMethod Thread Pre - " + GetNativeThreadId(System.Threading.Thread.CurrentThread)); 
     await Task.Delay(4000).ConfigureAwait(false); 
     Console.WriteLine("AsyncMethod Thread Post - " + GetNativeThreadId(System.Threading.Thread.CurrentThread)); 
    } 

Выход:

Main Thread Pre - 8652 
AsyncMethod Thread Pre - 4764 
AsyncMethod Thread Post - 1768 
Main Thread Post - 8652 

Использование Concurrency Visualizer, я могу видеть, что во время второй задержки 4, поток 4764 застрял в синхронизации , В конце концов он отключается основным потоком при выключении.

Не следует ли возвращать 4764 на ThreadPool после того, как он попадает в await? (При этом я не знаю, как это выглядело бы в конвергентном визуализаторе)

+2

Можете ли вы продемонстрировать какую-либо проблему *, не прибегая к визуализатору параллелизма? –

+0

Нет, я не могу.Я сделал это как упражнение, чтобы увидеть, что ожидаемая нить похожа на визуализатор параллелизма, и была удивлена, увидев, что он сидит в категории «Синхронизация», когда он ждет. Либо мое понимание асинхронности неверно, либо я неверно истолковываю то, что говорит мне визуализатор параллелизма. Полагаю, я просто ищу подтверждение, что поток 4764 действительно должен быть возвращен в ThreadPool, когда он попадает в ожидании. – Cuthbert

+0

Задание задачи задерживается, но вызов Delay по-прежнему внутренне запускает таймер и, как таковая, выполняет работу. Задержка не является задержкой планирования. –

ответ

4

Не следует ли возвращать поток 4764 в ThreadPool после того, как он достигнет ожидания?

Да. И это.

(Это, как говорится, я не знаю, что это будет выглядеть как внутри параллелизмом визуализатора)

Это достаточно легко проверить. Просто явно выполните некоторый код в пуле потоков и посмотрите, как выглядит этот поток в визуализаторе, когда он не занят.

Например:

ThreadPool.QueueUserWorkItem(o => 
    { 
     Console.WriteLine("worker: " + GetNativeThreadId(System.Threading.Thread.CurrentThread)); 
     Thread.Sleep(250); 
    }); 

(я добавил сон, так что показывает более легко в визуализатор, как, сделав что-то :)).

И когда вы это сделаете, вы увидите, что похоже на то, что вы видите. :)


Когда я запускал это, пул потоков даже использовал тот же рабочий поток, что и для оригинала Task. И вы можете видеть, что рабочий поток потока потока находится в состоянии Synchronization, пока он ждет больше работы.

Это имеет смысл. На абстрактном уровне, что делает поток пула? Все дело в том, что у него есть потоки, которые уже существуют. Но вы не хотите, чтобы эти потоки были там , работающие, если у них нет над чем работать. Это сэкономит время процессора без причины. Поэтому вместо этого они ждут объекта синхронизации.

Когда что-то (например, Task) хочет использовать его, он ставит в очередь рабочий элемент, а затем пул потоков подает сигнал в поток, ему нужно что-то делать. Это пробуждает поток, он выполняет свою работу, а затем снова блокирует объект синхронизации, ожидая чего-то другого.

Если вы проверяете стек вызовов для соответствующих потоков, вы увидите рабочий поток, ожидающий вызова WaitForSingleObject(), и вы увидите, что пул потоков в конечном счете разблокирует поток, используя ReleaseSemaphore().

И это отображается как состояние Synchronization для потока потока потока, как вы видели.

Смежные вопросы