Останавливающиеся и пробуждающиеся потоки

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

Если поток просто желает приостановить себя на предопределенный период времени, он может вызвать статический метод Thread.Sleep и перейти в состояние WaitSleepJoin.

Единственный параметр, передаваемый методу Sleep — это количество миллисекунд, в течение которых поток должен находиться в спящем состоянии. После вызова этот метод заставляет поток отдать остаток кванта времени, выделенного ему процессором, и отправиться спать.

По истечении указанного времени планировщик потоков возобновит его выполнение. Естественно, промежуток времени, который передается Sleep, довольно точен, но не абсолютно. Это связано с тем, что в конце периода поток не получает немедленно времени процессора.

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

Если вы обнаружите, что для решения проблемы синхронизации в коде нужно применить Sleep, знайте — подобным образом проблема не решится вообще. Она будет закамуфлирована и тем самым только усугубится.

Чтобы усыпить поток навсегда, предусмотрено специальное значение Timeout.Infinite, которое можно передать Sleep. Разбудить спящий поток можно, прервав его методом экземпляра Thread.Interrupt. Этот метод подобен Abort в том, что он пробуждает целевой поток и генерирует исключение ThreadlnterruptedException.

Поэтому, если функция потока не оснащена обработчиком этого исключения, оно будет распространяться по стеку до тех пор, пока выполнение потока не прекратится из-за необработанного исключения.

Если действительно необходимо реализовать изменяемое состояние сна, используя этот механизм, то для подстраховки потребуется поместить вызов Sleep внутрь блока try и перехватывать исключение ThreadlnterruptException.

В отличие от ThreadAbortException, исключение ThreadlnterruptException автоматически не повторяется исполняющей средой в конце обработчика. Однако обратите внимание, что реализовывать изменяемый механизм сна никогда не придется, потому что тип Monitor, который описывается ниже, предлагает более эффективный путь достижения той же цели.

Еще одним специальным значением параметра для Thread.Sleep является 0. Если передается 0, то метод Thread.Sleep заставит поток отдать остаток выделенного ему кванта времени. Возобновление выполнения такого потока произойдет тогда, когда планировщик потоков вернется к нему снова. При работе на платформе .NET 4.0 или последующей версии вместо этого следует использовать метод Thread.Yield.

Другой способ отправить поток в состояние сна на неопределенное время предоставляет метод экземпляра Thread. Suspend. Вызов Suspend приостанавливает выполнение потока до тех пор, пока оно не будет явно возобновлено. Возобновить выполнение потока можно вызовом метода экземпляра Resume или Interrupt.

Однако в случае применения Interrupt целевой поток должен иметь корректный обработчик исключений вокруг вызова Suspend, иначе поток может быть завершен.

Формально вызов Abort также возобновляет выполнение потока, но лишь затем, чтобы отправить ему ThreadAbortException и, в конечном счете, заставить завершиться. Имейте в виду, что любой поток с достаточными привилегиями может вызвать Suspend на потоке — даже текущий поток на самом себе.

Если текущий поток вызывает Suspend, в этой точке он блокируется, ожидая следующего вызова Resume.

Важно отметить, что когда на потоке вызывается Suspend, поток не приостанавливается немедленно. Вместо этого потоку разрешено выполняться до т.н. безопасной точки. Достигнув этой точки, поток приостанавливается. Безопасная точка — это место в управляемом коде, где можно провести сборку мусора.

Например, если среда CLR определяет, что пришло время для сборки мусора, она должна временно приостановить все потоки, пока это не будет сделано (т.е. собран мусор). Однако если поток находится на полпути в операции, которая состоит из нескольких инструкций, обращающихся к объекту в куче, а в это время сработает сборщик мусора и переместит этот объект в другое место системной памяти, ничего хорошего не произойдет.

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

Именно поэтому вызов Suspend позволяет потоку достичь безопасной точки, прежде чем поток в действительности будет приостановлен. Также необходимо подчеркнуть, что никогда не следует применять Suspend и Resume для управления синхронизацией. Разумеется, тот факт, что система позволяет потоку продолжать выполнение до достижения безопасной точки — достаточно веская причина полагаться на этот механизм, но, тем не менее, это — плохая практика дизайна.

Ожидание завершения потока

В предшествующих примерах метод Join использовался для ожидания завершения определенного потока. Фактически именно для этого он и предназначен. В неуправляемом приложении Win32, возможно, вы привыкли ожидать, пока дескриптор потока не даст сигнал о завершении потока.

Метод Join на самом деле реализует тот же механизм. Имя метода отражает тот факт, что путь выполнения текущего потока присоединяется к пути выполнения того потока, на котором вызван Join, и работа не может быть продолжена, пока это соединение не произойдет.

Естественно, вы захотите избежать вызова Join на текущем потоке. Эффект от этого подобен вызову Suspend из текущего потока — поток блокируется до тех пор, пока не будет прерван. Даже когда поток блокируется вызовом Join, он может быть разбужен вызовом Interrupt или Abort.

Иногда Join будет вызываться для ожидания завершения другого потока, но наверняка не захочется ждать вечно. Для Join предусмотрены перегрузки, позволяющие определять время ожидания.

Эти перегрузки возвращают булевское значение, причем true означает, что поток был остановлен, a false свидетельствует об окончании таймаута.

Вы можете следить за любыми ответами на эту запись через RSS 2.0 ленту. Вы можете оставить ответ, или trackback с вашего собственного сайта.

Оставьте отзыв

XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
Rambler's Top100