Российский Государственный Технологический Университет им. К.Э. Циолковского
Опубликован: 02.03.2007 | Доступ: свободный | Студентов: 5636 / 790 | Оценка: 3.96 / 3.45 | Длительность: 27:04:00
ISBN: 978-5-9556-0086-4
Лекция 15:

Потоки

Характеристики точки входа дополнительного потока

Сигнатура точки входа в поток определяется характеристиками класса-делегата:

public delegate void ThreadStart();

Класс-делегат здесь С ПУСТЫМ СПИСКОМ параметров! Очевидно, что точка входа обязана соответствовать этой спецификации. У функции, представляющей точку входа, должен быть ТОТ ЖЕ САМЫЙ список параметров! То есть ПУСТОЙ.

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

Запуск вторичных потоков

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

Вторичный поток также вправе поинтересоваться о собственном имени. Надо всего лишь расположить этот код в правильном месте. И чтобы он выполнился в правильное время:

using System;
using System.Threading;

namespace ThreadApp_1
{
// Рабочий класс. Делает себе свое ДЕЛО... 
class Worker
{
int allTimes;
int n;
// Конструктор умолчания...
public Worker()
{
n = 0;
allTimes = 0;	
}
// Конструктор с параметрами...
public Worker(int nKey, int tKey)
{
n = nKey;
allTimes = tKey;	
}

// Тело рабочей функции...	
public void DoItEasy()
{//====================================
int i;
for (i = 0; i < allTimes; i++)
{
if (n == 0)
Console.Write("{0,25}\r",i);
else 	
Console.Write("{0,10}\r",i);
}	
Console.WriteLine("\nWorker was here!");	
}//====================================
}

class StartClass
{
static void Main(string[] args)
{
Worker w0 = new Worker(0,100000);
Worker w1 = new Worker(1,100000);
ThreadStart t0, t1;
t0 = new ThreadStart(w0.DoItEasy);
t1 = new ThreadStart(w1.DoItEasy);
Thread th0, th1;
th0 = new Thread(t0);
th1 = new Thread(t1);
// При создании потока не обязательно использовать делегат.
// Возможен и такой вариант. Главное — это сигнатура функции.
// th1 = new Thread(w1.DoItEasy);
th0.Start();
th1.Start();
}
}
}
Листинг 15.3.

Важно! Первичный поток ничем не лучше любых других потоков приложения. Он может скоропостижно завершиться раньше всех им же порожденных потоков! Приложение же завершается после выполнения ПОСЛЕДНЕЙ команды в ПОСЛЕДНЕМ выполняемом потоке. Неважно, в каком.

Приостановка выполнения потока

Обеспечивается статическим методом Sleep(). Метод статический – это значит, что всегда производится не "усыпление", а "САМОусыпление" выполняемого в данный момент потока. Выполнение методов текущего потока блокируется на определенные интервалы времени. Все зависит от выбора перегруженного варианта метода. Планировщик потоков смотрит на поток и принимает решение относительно того, можно ли продолжить выполнение усыпленного потока.

В самом простом случае целочисленный параметр определяет временной интервал блокировки потока в миллисекундах.

Если значение параметра установлено в 0, поток будет остановлен до того момента, пока не будет предоставлен очередной интервал для выполнения операторов потока.

Если значение интервала задано с помощью объекта класса TimeSpan, то момент, когда может быть возобновлено выполнение потока, определяется с учетом закодированной в этом объекте информации:

// Поток заснул на 1 час, 2 минуты, 3 секунды:
 Thread.Sleep(new TimeSpan(1,2,3)); 
 :::::
 // Поток заснул на 1 день, 2 часа, 3 минуты, 4 секунды, 5 миллисекунд:
 Thread.Sleep(new TimeSpan(1,2,3,4,5));

Значение параметра, представленное выражением

System.Threading.Timeout.Infinite

позволяет усыпить поток на неопределенное время. А разбудить поток при этом можно с помощью метода Interrupt(), который в этом случае вызывается из другого потока:

using System;
 using System.Threading;

class Worker
 {
 int allTimes;
 // Конструктор с параметрами...
 public Worker(int tKey)
 {
 allTimes = tKey;	
 }

 // Тело рабочей функции...	
public void DoItEasy()
 {//====================================
 int i;
 for (i = 0; i < allTimes; i++)
 {
 Console.Write("{0}\r",i);
 if (i == 5000)
 {
 try
 {
 Console.WriteLine("\nThread go to sleep!");
 Thread.Sleep(System.Threading.Timeout.Infinite);
 }
 catch (ThreadInterruptedException e)
 {
 Console.WriteLine("{0}, {1}",e,e.Message);	
 }	
 }
 }	
Console.WriteLine("\nWorker was here!");	
 }//====================================
 }

class StartClass
 {
 static void Main(string[] args)
 {
 Worker w0 = new Worker(10000);
 ThreadStart t0;
 t0 = new ThreadStart(w0.DoItEasy);
 Thread th0;
 th0 = new Thread(t0);
 th0.Start();
 Thread.Sleep(10000);
 if (th0.ThreadState.Equals(ThreadState.WaitSleepJoin)) th0.Interrupt();
 Console.WriteLine("MainThread was here...");
 }
 }
Листинг 15.4.

И всегда надо помнить: приложение выполняется до тех пор, пока не будет выполнен последний оператор последнего потока. И неважно, выполняются ли при этом потоки, "спят" либо просто заблокированы.

Отстранение потока от выполнения

Обеспечивается нестатическим методом Suspend(). Поток входит в "кому", из которой его можно вывести, вызвав метод Resume(). Этот вызов, естественно, должен исходить из другого потока. Если все не отстраненные от выполнения потоки оказались завершены и некому запустить отстраненный потокприложение в буквальном смысле "зависает". Операторы в потоке могут выполняться, а выполнить их невозможно по причине отстранения потока от выполнения. Вот приложение и зависает...

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(25);
 }
 	
Console.WriteLine("Thread - still alive and working."); 
Console.WriteLine("Thread - finished working.");
 }
 }

class ThreadAbortTest 
 {
 public static void Main() 
 {
 ThreadStart myThreadDelegate = new ThreadStart(ThreadWork.DoWork);
 Thread myThread = new Thread(myThreadDelegate);
 myThread.Start();
 Thread.Sleep(10);
 Console.WriteLine("Main - aborting my thread.");
 myThread.Suspend();
 Console.WriteLine("Main ending."); 
 }
 }

Ко всему прочему следует иметь в виду, что метод Suspend() уже устарел...

"System.Threading.Thread.Suspend() is obsolete: Thread.Suspend has been deprecated. Please use other classes in System.Threading such as Monitor, Mutex, Event, and Semaphore to synchronize thread or project resources..."

Не следует использовать методы Suspend и Resume для синхронизации активности потоков. Просто в принципе ничего нельзя будет сделать, когда код в выполняемом потоке будет приостановлен с помощью метода Suspend(). Одним из нежелательных последствий подобного устранения от выполнения может оказаться взаимная блокировка потоков. Deadlocks can occur very easily.

kewezok kewezok
kewezok kewezok
Елена Шляхт
Елена Шляхт
Объясните плиз в чем отличие а++ от ++а
Почему результат разный?
int a=0, b=0;
Console.WriteLine(a++); //0
Console.WriteLine(++b); //1
a++;
++b;
Console.WriteLine(a); //2
Console.WriteLine(b); //2