Учебно-методический комплекс общее количество часов 58 ч. Лекции 28 ч


Download 2.46 Mb.
bet69/91
Sana19.10.2023
Hajmi2.46 Mb.
#1709453
TuriУчебно-методический комплекс
1   ...   65   66   67   68   69   70   71   72   ...   91
Bog'liq
Язык программирования C#

15. МНОГОПОТОЧНОЕ ПРОГРАММИРОВАНИЕ


15.1. Понятия процесса и потока

Как и все современные операционные системы, Windows поддерживает многозадачность и многопоточность. Это означает, что пользователь может запустить несколько приложений одновременно, и ОС так разделит между ними ресурсы системы так, что у пользователя сложиться впечатление, будто все приложения работают одновременно, даже если имеется только один физический процессор.


Каждое запущенное приложение является процессом (Process) в системе. Полный список всех запущенных процессов можно увидеть, запустив Диспетчер Задач (Task Manager) и перейдя в нем на вкладку Процессов (Processes). Запустить диспетчер задач можно, нажав на небезызвестную комбинацию Ctrl+Alt+Del.
Процесс, в свою очередь, состоит из потоков (Thread), позволяющих распараллелить работу конкретного приложения. Например, пока я набираю этот текст в Word’е, он параллельно со мной проверяет ранее набранный текст на ошибки. Это ставится возможным благодаря тому, что вывод данных с клавиатуры и проверка орфографии осуществляются независящими друг от друга потоками.
При запуске приложения создается процесс, под который выделяются системные ресурсы, в частности виртуальное адресное пространство. Сразу же после запуска процесса в нем создается главный (первый) поток, т.е. работающее приложение всегда имеет хотя бы один поток выполнения. Главный поток может затем породить другие вспомогательные потоки, которые сами могут также порождать потоки. Все потки в рамках одного процесса имеют общую кучу и адресное пространство, но для каждого потока выделяется собственный стек.


15.2. Создание потоков

Все необходимое для работы с патоками находится в пространстве имен System.Threading. В среде CLR работа с потоками основана на использовании класса System.Threading.Thread. Этот класс используется как для создания, так и для управления работой потока. Фактически каждый поток является объектом класса System.Threading.Thread. Новый поток создается существующим посредством создания очередного объекта этого класса.


Для создания объектов класса Tread определено четыре конструктора, самый употребительный из которых ожидает один параметр-делегат, указывающий на метод, не ожидающий и не возвращающий значений. Этот делегат определен в пространстве имен System.Threading и имеет следующий вид.

public delegate void ThreadStart ();


Лист. 6.1

Делегат, переданный конструктору объекта класса Thread, определяет функцию, с которой и будет начинаться выполнение потока, а с её завершением работа потока будет прекращена. Следующий ниже код создаёт новый поток. Сразу после своего создания поток работать не начинает, и чтобы его запустись необходимо вызвать метод Start() объекта потока. Вызов метода Start() начинает выполнение потока. Поток продолжается до выхода из исполняемого метода. Приведем пример простейшего многопоточного приложения.


using System;


using System.Threading;

namespace TheSimplest


{
class Program
{
static void Main()
{
// Созданть нового потока на осонове WriteY
Thread t = new Thread(WriteY);
// Запустить WriteY в новом потоке
t.Start();
// Все время печатать 'x'
while (true)
Console.Write("x");
}

static void WriteY()


{
// Все время печатать 'y'
while (true)
Console.Write("y");
}
}
}
Лист. 5.2

Эта программа просто выводит хаотические комбинации из символов ‘x’ и ‘y’. Начинается она с создания нового объекта t класса Thread, т.е. создания нового потока. Для этого используется метод WriteY, сигнатура которого соответствует делегату ThreadStart. Сам метод WriteY только выводит символ ‘y’ в бесконечном цикле. Далее созданный процесс запускается, для этого запускается его метод Start(). После этого выполнение программы продолжается и доходит до бесконечного цикла, выводящего символ ‘x’. Фактически теперь работают два независимых потока: один выводит ‘x’, другой – ‘у’ и каждому из них выделяется процессорное время.


Вывод программы:

xxxxxxxxxxxxxyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
yyyyyyyyyyyyyxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
Вывод. 5.1

Усложним нашу задачу, введя некоторое подобие синхронизации между потоками. Наша программа должна будет выводить на консоль простой геометрический узор из двух элментов.


using System;


using System.Threading;
class ArrowPattern
{
string Tail = "| |";
string Arrow = @"\ /";

volatile bool _ToRun;


public bool ToRun


{
set
{
_ToRun = value;
}
}

public void Run()


{
_ToRun = true;
while (_ToRun)
{
Console.WriteLine(Tail);
Console.WriteLine(Arrow);
}
}
}

class Program


{
static void Main(string[] args)
{
ArrowPattern Pattern = new ArrowPattern();

Thread PatternThread = new Thread(new ThreadStart(Pattern.Run));


PatternThread.Name = "PatternThread";


PatternThread.Start();

int Counter = 0;


while (PatternThread.IsAlive)
{
Thread.Sleep(100);
Counter++;
if (Counter == 10)
Pattern.ToRun = false;
}
}
}
Лист. 5.3

Первым в листинге определяется небольшой класс ArrowPattern. В классе объявлены элементы узора – это две строки Tail и Aroow, также в нем объявлена приватная переменная _ToRun, используемая, для синхронизации работы класса. Переменная _ToRun объявлена с модификаторм volatile, который разрешает доступ к ней из разных потоков. Для доступа к переменной _ToRun используется свойство ToRun, открытое только для записи.


volatile bool _ToRun;


Лист. 5.4

Класс ArrowPattern содержит единственный метод, специально приспособленный для вызова в качестве стартера потока. Этот метод выводит узор пока _ToRun установлен в True.


public void Run()


Лист. 5.5

Теперь в главном потоке программы, в методе Main создаем и запускаем рабочий поток PatternThread. Перед запуском потока мы присваиваем ему имя. Поток можно поименовать, используя свойство Name. Это предоставляет большое удобство при отладке: имена потоков можно вывести в Console.WriteLine и увидеть в окне Debug – Threads в Microsoft Visual Studio. Имя потоку может быть назначено в любой момент, но только один раз – при попытке изменить его будет сгенерировано исключение. Главному потоку приложения также можно назначить имя, используя статическое свойство Thread.Name.


ArrowPattern Pattern = new ArrowPattern();


Thread PatternThread = new Thread(new ThreadStart(Pattern.Run));

tternThread.Name = "PatternThread";


PatternThread.Start();
Лист. 5.6

Далее мы контролируем работу потока, выполняя цикл контролирующий останов запущенного потока ровно 11 раз. Цикл контролируется счетчиком Counter и переменной PatternThread.IsAlive, определяющей работает ли еще запущенный процесс. Чтобы дать возможность порожденному потоку вывести большее количество символов основный поток приостанавливает свое выполнение на 100 миллисекунд при каждой итерации цикла, для этого используется метод Thread.Sleep(). И на конец дочерний процесс останавливается присвоением свойству Pattern.ToRun значения false.


Когда потоку необходимо контролировать свое собственное выполнение, например как в нашем случае приостановив работу, он может вызвать статические методы класса Thread.

int Counter = 0;


while (PatternThread.IsAlive)
{
Thread.Sleep(100);
Counter++;
if (Counter == 10)
Pattern.ToRun = false;
}
Лист. 5.7

Вывод этой программы будет разным на разных машинах, но состоять он будет из разного числа элементов узора вида.


| |
\ /


Вывод. 5.2

Download 2.46 Mb.

Do'stlaringiz bilan baham:
1   ...   65   66   67   68   69   70   71   72   ...   91




Ma'lumotlar bazasi mualliflik huquqi bilan himoyalangan ©fayllar.org 2024
ma'muriyatiga murojaat qiling