Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методичка для КР по ООП.doc
Скачиваний:
8
Добавлен:
18.04.2019
Размер:
2.47 Mб
Скачать

Игры с потоками

Но сначала о том, что должно происходить в потоке. Выполнение текущего потока предполагает выполнение программного кода.

Откуда этот код? От функций, естественно. От статических и нестатических методов-членов класса. И запустить поток можно единственным способом – указав точку входа потока - метод, к выполнению операторов которого должен приступить запускаемый поток.

Точкой входа ПЕРВИЧНОГО потока являются СТАТИЧЕСКИЕ функции Main или WinMain. Точнее, первый оператор метода.

Точка входа ВТОРИЧНОГО потока назначается при создании потока.

А дальше – как получится. Поток выполняется оператор за оператором. Со всеми циклами, заходами в вызываемые функции, в блоки свойств, в операторы конструкторов… И так продолжается до тех пор, пока не возникнет:

  • неперехваченное исключение,

  • не будет достигнут конец цепочки операторов (последний оператор в функциях Main или WinMain),

  • не будет отработан вызов функции, обеспечивающей прекращение выполнения потока.

В силу того, что первичный поток создаётся и запускается автоматически (без какого-либо особого участия со стороны программиста), то и заботиться в случае простого однопоточного приложения не о чем.

При создании многопоточного приложения забота о создании дополнительных потоков – забота программиста. Здесь всё надо делать своими руками.

Деятельность по созданию потока предполагает три этапа:

  • определение метода, который будет играть роль точки входа в поток,

  • создание объекта-представителя специального класса-делегата (ThreadStart class), который настраивается на точку входа в поток,

  • создание объекта-представителя класса потока. При создании объекта потока конструктору потока передаётся в качестве параметра ссылка на делегата, настроенного на точку входа.

Замечание. Точкой входа в поток не может быть конструктор, поскольку не существует делегатов, которые могли бы настраиваться на конструкторы.

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

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

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,-25}\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);

th0.Start();

th1.Start();

}

}

}

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