Потоки
Завершение потоков
- Первый вариант остановки потока тривиален. Поток завершается после выполнения ПОСЛЕДНЕГО оператора выполняемой цепочки операторов. Допустим, в ходе выполнения условного оператора значение некоторой переменной сравнивается с фиксированным значением и в случае совпадения значений управление передается оператору return:
for (x=0;;x++) { if (x==max) return; // Все. Этот оператор оказался последним. else { :::::::::: } }
- Поток может быть остановлен в результате выполнения метода Abort(). Эта остановка является достаточно сложным делом.
- При выполнении этого метода происходит активация исключения ThreadAbortException. Естественно, это исключение может быть перехвачено в соответствующем блоке catch. Во время обработки исключения допустимо выполнение самых разных действий, которые осуществляются в этом самом "остановленном" потоке. В том числе возможна и реанимация остановленного потока путем вызова метода ResetAbort().
- При перехвате исключения CLR обеспечивает выполнение операторов блоков finally, которые выполняются все в том же потоке.
Таким образом, остановка потока путем вызова метода Abort не может рассматриваться как НЕМЕДЛЕННАЯ остановка выполнения потока:
using System; using System.Threading; public class ThreadWork { public static void DoWork() { int i; try { for(i=0; i<100; i++) { //4. Вот скромненько так работает... // Поспит немножко – а потом опять поработает. // Take Your time! 100 раз прокрутиться надо // вторичному потоку до нормального завершения. Console.WriteLine("Thread – working {0}.", i); Thread.Sleep(10); } } catch(ThreadAbortException e) { //6. //– Ну дела! А где это мы... Console.WriteLine("Thread – caught ThreadAbortException – resetting."); Console.WriteLine("Exception message: {0}", e.Message); // (Голос сверху) //– Вы находитесь в блоке обработки исключения, связанного с // непредвиденным завершением потока. //– Понятно... Значит, не успели. "Наверху" сочли нашу деятельность // нецелесообразной и не дали (потоку) завершить до конца начатое дело! Thread.ResetAbort(); // (Перехватывают исключение и отменяют остановку потока) // Будем завершать дела. Но будем делать это как положено, // а не в аварийном порядке. Нам указали на дверь, но мы // уходим достойно! // (Комментарии постороннего) // А чтобы стал понятен альтернативный исход – надо // закомментировать строку с оператором отмены остановки потока. } finally { //7. //– Вот где бы мы остались, если бы не удалось отменить // остановку потока! finally блок... Отстой! Console.WriteLine("Thread – in finally statement."); } //8. // – А вот преждевременный, но достойный уход. // Мы не довели дело до конца только потому, что нам не дали // сделать этого. Обстоятельства бывают выше. Уходим достойно. Console.WriteLine("Thread – still alive and working."); Console.WriteLine("Thread – finished working."); } } class ThreadAbortTest { public static void Main() { //1. Мероприятия по организации вторичного потока! ThreadStart myThreadDelegate = new ThreadStart(ThreadWork.DoWork); Thread myThread = new Thread(myThreadDelegate); //2. Вторичный поток стартовал! myThread.Start(); //3. А вот первичный поток – самоусыпился! // И пока первичный поток спит, вторичный поток – работает! Thread.Sleep(50); //5. Но вот первичный поток проснулся – и первое, что он // делает, – это прерывает вторичный поток! Console.WriteLine("Main – aborting my thread."); myThread.Abort(); //9. А в столицах тоже все дела посворачивали... Console.WriteLine("Main ending."); } }
Листинг 15.5.
Метод Join()
Несколько потоков выполняются "параллельно" в соответствии с предпочтениями планировщика потоков. Нестатический метод Join() позволяет изменить последовательность выполнения потоков многопоточного приложения. Метод Join() выполняется в одном из потоков по отношению к другому потоку.
В результате выполнения этого метода данный текущий поток немедленно блокируется до тех пор, пока не завершит свое выполнение поток, по отношению к которому был вызван метод Join.
Перегруженный вариант метода имеет целочисленный аргумент, который воспринимается как временной интервал. В этом случае выполнение текущего потока может быть возобновлено по истечении этого периода времени до завершения этого потока:
using System; using System.Threading; public class ThreadWork { public static void DoWork() { for(int i=0; i<10; i++) { Console.WriteLine("Thread – working."); Thread.Sleep(10); } Console.WriteLine("Thread – finished working."); } } class ThreadTest { public static void Main() { ThreadStart myThreadDelegate = new ThreadStart(ThreadWork.DoWork); Thread myThread = new Thread(myThreadDelegate); myThread.Start(); Thread.Sleep(100); myThread.Join(); // Закомментировать вызов метода и осознать разницу. Console.WriteLine("Main ending."); } }
Состояния потока (перечисление ThreadState)
Класс ThreadState определяет набор всех возможных состояний выполнения для потока. После создания потока и до завершения он находится по крайней мере в одном из состояний. Потоки, созданные в общеязыковой среде выполнения, изначально находятся в состоянии Unstarted, в то время как внешние потоки, приходящие в среду выполнения, находятся уже в состоянии Running. Потоки с состоянием Unstarted переходят в состояние Running при вызове метода Start. Не все комбинации значений ThreadState являются допустимыми; например, поток не может быть одновременно в состояниях Aborted и Unstarted.
В следующей таблице перечислены действия, вызывающие смену состояния.
Действие | Состояние потока |
---|---|
Поток создается в среде CLR | Unstarted |
Поток вызывает метод Start | Running |
Поток начинает выполнение | Running |
Поток вызывает метод Sleep | WaitSleepJoin |
Поток вызывает метод Wait для другого объекта | WaitSleepJoin |
Поток вызывает метод Join для другого потока | WaitSleepJoin |
Другой поток вызывает метод Interrupt | Running |
Другой поток вызывает метод Suspend | SuspendRequested |
Поток отвечает на запрос метода Suspend | Suspended |
Другой поток вызывает метод Resume | Running |
Другой поток вызывает метод Abort | AbortRequested |
Поток отвечает на запрос метода Abort | Stopped |
Поток завершен | Stopped |
Начальное состояние потока (если это не главный поток), в котором он оказывается непосредственно после его создания, – Unstarted. В этом состоянии он пребывает до тех пор, пока вызовом метода Start() не будет переведен в состояние Running.
В дополнение к вышеперечисленным состояниям существует также Background – состояние, которое указывает, выполняется ли поток на фоне или на переднем плане.
Свойство Thread.ThreadState потока содержит текущее состояние потока. Для определения текущего состояния потока в приложении можно использовать битовые маски. Пример условного выражения:
if((myThread.ThreadState & (ThreadState.Stopped | ThreadState.Unstarted))==0) {...}
Члены перечисления:
Имя члена | Описание | Значение |
---|---|---|
Running | Поток был запущен, он не заблокирован, и нет задерживающегося объекта ThreadAbortException | 0 |
StopRequested | Поток запрашивается на остановку. Это только для внутреннего использования | 1 |
SuspendRequested | Запрашивается приостановка работы потока | 2 |
Background | Поток выполняется как фоновый, что является противоположным к приоритетному потоку. Это состояние контролируется заданием свойства Thread.IsBackground | 4 |
Unstarted | Метод Thread.Start не был вызван для потока | 8 |
Stopped | Поток остановлен | 16 |
WaitSleepJoin | Поток заблокирован в результате вызова к методам Wait, Sleep или Join | 32 |
Suspended | Работа потока была приостановлена | 64 |
AbortRequested | Метод Thread.Abort был вызван для потока, но поток еще не получил задерживающийся объект System.Threading.ThreadAbortException, который будет пытаться завершить поток | 128 |
Aborted | Поток находится в Stopped -состоянии | 256 |