Потоки
Завершение потоков
- Первый вариант остановки потока тривиален. Поток завершается после выполнения ПОСЛЕДНЕГО оператора выполняемой цепочки операторов. Допустим, в ходе выполнения условного оператора значение некоторой переменной сравнивается с фиксированным значением и в случае совпадения значений управление передается оператору 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 |
