Создание потока

Потоки

Выполнение кода в С# всегда начинается с метода Main. Метод Main - образует главный поток приложения. Поток представляет собой последовательность операций исполняемых в программе. Главный поток может запустить несколько подчиненных или говорят рабочих потоков. В каждом потоке выполняется одна, какая либо функция. Говорят, что в этом случае программа распараллеливается, т. е. распадается на несколько потоков, которые выполняются параллельно. На самом деле на однопроцессорной ЭВМ все потоки выполняются небольшими опциями последовательно друг за другом, за счет чего у пользователя создается иллюзия их параллельной работы. Реальный выигрыш по времени получается только когда потоки используют разные аппаратные ресурсы ЭВМ или/и на многопроцессорных машинах.

Многопоточная система инкапсулирована в класс Thread. Данный класс объявлен как запечатанный (sealed) класс, то есть от него нельзя породить новый класс. В классе Thread определено ряд свойств и методов для управления потоками.

Для создания потока необходимо создать объект класса Thread.

Один из конструкторов этого класса имеет следующий вид:

public Thread (ThreadStart start)

Объект start типа ThreadStart должен содержать адрес точки входа в функцию, которая будет выполняться в потоке.

Тип ThreadStart объявлен в пространстве имен System.Threading с помощью ключевого слова delegate и имеет следующую сигнатуру:

public delegate void ThreadStart();

Согласно, сигнатуре объявления делегата, потоковая функция должна возвращать значение типа void и не принимать параметров.

Пусть, например, заголовок потоковой функции - void funcThread(), тогда создание объекта start:

ThreadStart start = new ThreadStart(funcThread);

Конструктору делегата ThreadStart при создании объекта передается имя функции, которая будет выполняться в потоке.

Объект start после своего создания хранит адрес точки входа в функцию funcThread.

Следующим шагом необходимо создать потоковый объект, передав конструктору класса Thread объект start в качестве параметра:

Thread thrd = new Thread(start);

После создания потокового объекта необходимо запустить потоковую функцию на выполнение путем вызова метода Start, используя ссылку thrd:

thrd.Start();

Потоковому объекту с помощью свойства Name можно присвоить некоторое имя:

thrd.Name = “ ThrName”;,

или прочесть:

strig nm = thrd.Name;

Поток считается активным, не зависимо от того находится ли он в состоянии останова или нет, до тех пор, пока не завершится запущенная в нем функция. После завершения потоковой функции поток уничтожается. Такое завершение работы потока считается нормальным. Узнать, находиться ли поток в активном состоянии или нет можно с помощью свойства IsAlive, доступного только для чтения. Свойство возвращает значение true, если поток активен и ложь (false), если нет. Это свойство можно использовать для определения момента окончания работы потока. Приостановить работу потока на определенное время можно с помощью статического метода:

public static void Sleep(int time)

класса Thread, где параметр time задает время в миллисекундах.

Для закрепления материала рассмотрим пример, демонстрирующий изложенный материал:

using System;

using System.Collections.Generic;

using System.Text;

using System.Threading;

namespace ConsoleApplication4

{

class MyThread

{

private int count; //счетчик

//объявление ссылки на потоковый объект

public Thread thrd;

public MyThread(string name)

{

count = 0;

//здесь создается потоковый объект для функции FnThr

thrd = new Thread(new ThreadStart(this.FnThr));

thrd.Name = name; // Устанавливаем имя потока.

thrd.Start(); // Запускаем поток на выполнение.

}

//определение потоковой функции

public void FnThr()

{

Console.WriteLine(thrd.Name + " стартовал.");

do

{

Thread.Sleep(300); //”засыпаем” на 0,3 сек

Console.WriteLine("В потоке " + thrd.Name + ", count - " + count);

count++;

} while (count <= 4);

Console.WriteLine(thrd.Name + " завершен.");

}

}

class Program

{

static void Main(string[] args)

{

Console.WriteLine("Главный поток стартовал.");

// Создаем массив ссылок на потоковые объекты

MyThread[] mt = new MyThread[5];

// Создаем объекты класса MyThread.

for (int j = 0; j < mt.Length; j++)

mt[j] = new MyThread("Потомок #" + j.ToString());

bool live;

do

{

live=false;

Console.Write(".");

Thread.Sleep(100); //”спит” главный поток

for (int j = 0; j < mt.Length; j++)

{

live = live | mt[j].thrd.IsAlive;

}

} while (live);

Console.WriteLine("Главный поток завершен.");

}

}

}

В этом приложении объявлен класс MyThread. Конструктор класса MyThread создает потоковый объект с помощью строки кода:

thrd = new Thread(new ThreadStart(this.FnThr));

Затем потоковому объекту присваивается имя, которое конструктор принимает в качестве параметра и запускается с помощью метода Start() поток, что приводит к вызову потокового метода FnThr().

В методе FnThr () организован цикл, который "считает" от 0 до 4. До входа в цикл метод выводит на консоль имя стартовавшего потока, а после окончания цикла – имя завершившего свою работу потока. Вызов метода Sleep(), в теле цикла метода FnThr () заставляет поток, из которого он был вызван, приостановить выполнение на период времени, заданный в миллисекундах.

В методе Main() при выполнении следующих инструкций создается пять объектов класса Thread:

// Создаем массив ссылок на потоковые объекты

MyThread[] mt = new MyThread[5];

// Создаем объекты класса MyThread.

for (int j = 0; j < mt.Length; j++)

mt[j] = new MyThread("Потомок #" + j.ToString());

После создания потоков и запуска их на выполнение в конструкторе класса MyThread, в приложении выполняются основной поток, а, именно, метод Main() и пять дочерних потоков.

С помощью цикла:

for (int j = 0; j < mt.Length; j++)

{

live = live | mt[j].thrd.IsAlive;

}

} while (live);

метод Main() дожидается завершения работы потоков.

В приведенном примере завершение работы потока определялось с помощью метода IsAlive(). Второй способ, которые позволяет "дождаться" завершения выполнения потока, состоит в вызове метода Join ().

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


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: