Многопоточность

Содержание

Слайд 2

ОСНОВНЫЕ ВОПРОСЫ

Введение в многопоточность;
Cоздание потоков;
Потоки с параметрами;
Синхронизация потоков;
Мониторы;
Класс AUTORESETEVENT;
Мьютексы;
Семафоры;
Использование таймеров.

ОСНОВНЫЕ ВОПРОСЫ Введение в многопоточность; Cоздание потоков; Потоки с параметрами; Синхронизация потоков;

Слайд 3

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD

Одним из ключевых аспектов в современном программировании является

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD Одним из ключевых аспектов в современном программировании
многопоточность. Ключевым понятием при работе с многопоточностью является поток. Поток представляет некоторую часть кода программы. При выполнении программы каждому потоку выделяется определенный квант времени. И при помощи многопоточности мы можем выделить в приложении несколько потоков, которые будут выполнять различные задачи одновременно. Если у нас, допустим, графическое приложение, которое посылает запрос к какому-нибудь серверу или считывает и обрабатывает огромный файл, то без многопоточности у нас бы блокировался графический интерфейс на время выполнения задачи. А благодаря потокам мы можем выделить отправку запроса или любую другую задачу, которая может долго обрабатываться, в отдельный поток. Поэтому, к примеру, клиент-серверные приложения (и не только они) практически не мыслимы без многопоточности. Таким образом, многопоточная обработка является особой формой много­задачности.

Слайд 4

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD

Образно многопоточность на базе потоков можно изобразить следующим

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD Образно многопоточность на базе потоков можно изобразить следующим образом:
образом:

Слайд 5

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD

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

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD Разумеется, необходимо знать особенности одновременного выполнения множества
Из-за того, что они выполняются в одно и то же время, при получении ими доступа к одним и тем же данным могут возникать проблемы. Чтобы этого не происходило, должны быть реализованы механизмы синхронизации.
Поток (thread) представляет собой независимую последовательность инструкций в программе. Потоки играют важную роль как для клиентских, так и для серверных приложений. К примеру, во время ввода какого-то кода C# в окне редактора Visual Studio проводится анализ на предмет различных синтаксических ошибок. Этот анализ осуществляется отдельным фоновым потоком. То же самое происходит и в средстве проверки орфографии в Microsoft Word. Один поток ожидает ввода данных пользователем, а другой в это время выполняет в фоновом режиме некоторый анализ. Третий поток может сохранять записываемые данные во временный файл, а четвертый — загружать дополнительные данные из Интернета.

Слайд 6

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD

В приложении, которое функционирует на сервере, один поток

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD В приложении, которое функционирует на сервере, один
всегда ожидает поступления запроса от клиента и потому называется потоком-слушателем (listener thread). При получении запроса он сразу же пересылает его отдельному рабочему потоку (worker thread), который дальше сам продолжает взаимодействовать с клиентом. Поток-слушатель после этого незамедлительно возвращается к своим обязанностям по ожиданию поступления следующего запроса от очередного клиента. Каждый процесс состоит из ресурсов, таких как оконные дескрипторы, файловые дескрипторы и другие объекты ядра, имеет выделенную область в виртуальной памяти и содержит как минимум один поток. Потоки планируются к выполнению операционной системой. У любого потока имеется приоритет, счетчик команд, указывающий на место в программе, где происходит обработка, и стек, в котором сохраняются локальные переменные потока. Стек у каждого потока выглядит по-своему, но память для программного кода и куча разделяются среди всех потоков, которые функционируют внутри одного процесса. Это позволяет потокам внутри одного процесса быстро взаимодействовать между собой, поскольку все потоки процесса обращаются к одной и той же виртуальной памяти. Однако это также и усложняет дело, поскольку дает возможность множеству потоков изменять одну и ту же область памяти.

Слайд 7

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD

Различают две разновидности многозадачности: на основе процессов и

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD Различают две разновидности многозадачности: на основе процессов
на основе потоков. В связи с этим важно понимать отличия между ними.
Процесс отвечает за управление ресурсами, к числу которых относится виртуальная память и дескрипторы Windows, и содержит как минимум один поток. Наличие хотя бы одного потока является обязательным для выполнения любой программы. Поэтому многозадачность на основе процессов — это средство, благодаря которому на компьютере могут параллельно выполняться две программы и более.
Так, многозадачность на основе процессов позволяет одновременно выполнять программы текстового редактора, электронных таблиц и просмотра содержимого в Интернете. При организации многозадачности на основе процессов программа является наименьшей единицей кода, выполнение которой может координировать планировщик задач.

Слайд 8

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD

Поток представляет собой координируемую единицу исполняемого кода. Своим

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD Поток представляет собой координируемую единицу исполняемого кода.
происхождением этот термин обязан понятию "поток исполнения". При организации многозадачности на основе потоков у каждого процесса должен быть по крайней мере один поток, хотя их может быть и больше. Это означает, что в одной программе одновременно могут решаться две задачи и больше. Например, текст может форматироваться в редакторе текста одновременно с его выводом на печать, при условии, что оба эти действия выполняются в двух отдельных потоках.
Отличия в многозадачности на основе процессов и потоков могут быть сведены к следующему: многозадачность на основе процессов организуется для параллельного выполнения программ, а многозадачность на основе потоков — для параллельного выполнения отдельных частей одной программы.

Слайд 9

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD

Главное преимущество многопоточной обработки заключается в том, что

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD Главное преимущество многопоточной обработки заключается в том,
она позволяет писать программы, которые работают очень эффективно благодаря возможности выгодно использовать время простоя, неизбежно возникающее в ходе выполнения большинства программ. Как известно, большинство устройств ввода-вывода, будь то устройства, подключенные к сетевым портам, накопители на дисках или клавиатура, работают намного медленнее, чем центральный процессор (ЦП). Поэтому большую часть своего времени программе приходится ожидать отправки данных на устройство ввода-вывода или приема информации из него. А благодаря многопоточной обработке программа может решать какую-нибудь другую задачу во время вынужденного простоя.
Например, в то время как одна часть программы отправляет файл через соединение с Интернетом, другая ее часть может выполнять чтение текстовой информации, вводимой с клавиатуры, а третья — осуществлять буферизацию очередного блока отправляемых данных.

Слайд 10

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD

Поток может находиться в одном из нескольких состояний.

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD Поток может находиться в одном из нескольких
В целом, поток может быть выполняющимся; готовым к выполнению, как только он получит время и ресурсы ЦП; приостановленным, т.е. временно не выполняющимся; возобновленным в дальнейшем; заблокированным в ожидании ресурсов для своего выполнения; а также завершенным, когда его выполнение окончено и не может быть возобновлено.
В среде .NET Framework определены две разновидности потоков: приоритетный и фоновый. По умолчанию создаваемый поток автоматически становится приоритетным, но его можно сделать фоновым. Единственное отличие приоритетных потоков от фоновых заключается в том, что фоновый поток автоматически завершается, если в его процессе остановлены все приоритетные потоки.

Слайд 11

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD

В связи с организацией многозадачности на основе потоков

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD В связи с организацией многозадачности на основе
возникает потребность в особого рода режиме, который называется синхронизацией и позволяет координировать выполнение потоков вполне определенным образом. Для такой синхронизации в C# предусмотрена отдельная подсистема.
Все процессы состоят хотя бы из одного потока, который обычно называют основным, поскольку именно с него начинается выполнение программы. Из основного потока можно создать другие потоки.
В языке C# и среде .NET Framework поддерживаются обе разновидности многозадачности: на основе процессов и на основе потоков. Поэтому средствами C# можно создавать как процессы, так и потоки, а также управлять и теми и другими.
Намного более важной оказывается поддержка в C# многопоточной обработки, благодаря которой упрощается написание высокопроизводительных, многопоточных программ на C# по сравнению с некоторыми другими языками программирования.

Слайд 12

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD

Основной функционал для использования потоков в приложении сосредоточен

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD Основной функционал для использования потоков в приложении
в пространстве имен System.Threading. В нем определен класс, представляющий отдельный поток - класс Thread.
Класс Thread определяет ряд методов и свойств, которые позволяют управлять потоком и получать информацию о нем.

Слайд 13

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD

Основные свойства класса Thread:
Статическое свойство CurrentContext позволяет получить

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD Основные свойства класса Thread: Статическое свойство CurrentContext
контекст, в котором выполняется поток
Статическое свойство CurrentThread возвращает ссылку на выполняемый поток
Свойство IsAlive указывает, работает ли поток в текущий момент
Свойство IsBackground указывает, является ли поток фоновым
Свойство Name содержит имя потока
Свойство Priority хранит приоритет потока - значение перечисления ThreadPriority
Свойство ThreadState возвращает состояние потока - одно из значений перечисления ThreadState

Слайд 14

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD

Некоторые методы класса Thread:
Статический метод GetDomain возвращает ссылку

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD Некоторые методы класса Thread: Статический метод GetDomain
на домен приложения
Статический метод GetDomainId возвращает id домена приложения, в котором выполняется текущий поток
Статический метод Sleep останавливает поток на определенное количество миллисекунд
Метод Abort уведомляет среду CLR о том, что надо прекратить поток, однако прекращение работы потока происходит не сразу, а только тогда, когда это становится возможно. Для проверки завершенности потока следует опрашивать его свойство ThreadState
Метод Interrupt прерывает поток на некоторое время
Метод Join блокирует выполнение вызвавшего его потока до тех пор, пока не завершится поток, для которого был вызван данный метод
Метод Resume возобновляет работу ранее приостановленного потока
Метод Start запускает поток
Метод Suspend приостанавливает поток

Слайд 15

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD

Получение информации о потоке
Используем вышеописанные свойства и методы

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD Получение информации о потоке Используем вышеописанные свойства
для получения информации о потоке:

В этом случае мы получим примерно следующий вывод:
Так как по умолчанию свойство Name у объектов Thread не установлено, то в первом случае мы получаем в качестве значения этого свойства пустую строку.

Слайд 16

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD

Статус потока
Статусы потока содержатся в перечислении ThreadState:
Aborted: поток

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD Статус потока Статусы потока содержатся в перечислении
остановлен, но пока еще окончательно не завершен
AbortRequested: для потока вызван метод Abort, но остановка потока еще не произошла
Background: поток выполняется в фоновом режиме
Running: поток запущен и работает (не приостановлен)
Stopped: поток завершен
StopRequested: поток получил запрос на остановку
Suspended: поток приостановлен
SuspendRequested: поток получил запрос на приостановку
Unstarted: поток еще не был запущен
WaitSleepJoin: поток заблокирован в результате действия методов Sleep или Join

Слайд 17

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD

В процессе работы потока его статус многократно может

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD В процессе работы потока его статус многократно
измениться под действием методов. Так, в самом начале еще до применения метода Start его статус имеет значение Unstarted. Запустив поток, мы изменим его статус на Running. Вызвав метод Sleep, статус изменится на WaitSleepJoin. А применяя метод Abort, мы тем самым переведем поток в состояние AbortRequested, а затем Aborted, после чего поток окончательно завершится.

Слайд 18

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD

Приоритеты потоков
Приоритеты потоков располагаются в перечислении ThreadPriority:
Lowest
BelowNormal
Normal
AboveNormal
Highest
По умолчанию

ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ. КЛАСС THREAD Приоритеты потоков Приоритеты потоков располагаются в перечислении
потоку задается значение Normal. Однако мы можем изменить приоритет в процессе работы программы. Например, повысить важность потока, установив приоритет Highest. Среда CLR будет считывать и анализировать значения приоритета и на их основании выделять данному потоку то или иное количество времени.

Слайд 19

СОЗДАНИЕ ПОТОКОВ. ДЕЛЕГАТ THREADSTART

Используя класс Thread, мы можем выделить в приложении несколько

СОЗДАНИЕ ПОТОКОВ. ДЕЛЕГАТ THREADSTART Используя класс Thread, мы можем выделить в приложении
потоков, которые будут выполняться одновременно.
Во-первых, для запуска нового потока нам надо определить задачу в приложении, которую будет выполнять данный поток. Для этого мы можем добавить новый метод, производящий какие-либо действия.
Для создания нового потока используется делегат ThreadStart, который получает в качестве параметра метод, который мы определил выше.
И чтобы запустить поток, вызывается метод Start. Рассмотрим на примере:

Слайд 20

Здесь новый поток будет производить действия, определенные в методе Count. Чтобы запустить

Здесь новый поток будет производить действия, определенные в методе Count. Чтобы запустить
этот метод в качестве второго потока, мы сначала создаем объект потока: Thread myThread = new Thread(new ThreadStart(Count));. В конструктор передается делегат ThreadStart, который в качестве параметра принимает метод Count. И следующей строкой myThread.Start() мы запускаем поток. После этого управление передается главному потоку, и выполняются все остальные действия, определенные в методе Main.

Слайд 21

Таким образом, в нашей программе будут работать одновременно главный поток, представленный методом

Таким образом, в нашей программе будут работать одновременно главный поток, представленный методом
Main, и второй поток. Кроме действий по созданию второго потока, в главном потоке также производятся некоторые вычисления. Как только все потоки отработают, программа завершит свое выполнение.
Подобным образом мы можем создать и три, и четыре, и целый набор новых потоков, которые смогут решать те или иные задачи.

Слайд 22

СОЗДАНИЕ ПОТОКОВ. ДЕЛЕГАТ THREADSTART

Существует еще одна форма создания потока: Thread myThread =

СОЗДАНИЕ ПОТОКОВ. ДЕЛЕГАТ THREADSTART Существует еще одна форма создания потока: Thread myThread
new Thread(Count);
Хотя в данном случае явным образом мы не используем делегат ThreadStart, но неявно он создается. Компилятор C# выводит делегат из сигнатуры метода Count и вызывает соответствующий конструктор.

Слайд 23

ПОТОКИ С ПАРАМЕТРАМИ И PARAMETERIZEDTHREADSTART

В предыдущем примере мы рассмотрели, как запускать в

ПОТОКИ С ПАРАМЕТРАМИ И PARAMETERIZEDTHREADSTART В предыдущем примере мы рассмотрели, как запускать
отдельных потоках методы без параметров. А что, если нам надо передать какие-нибудь параметры в поток?
Для этой цели используется делегат ParameterizedThreadStart. Его действие похоже на функциональность делегата ThreadStart. Рассмотрим на примере:

Слайд 24

После создания потока мы передаем метод myThread.Start(number); переменную, значение которой хотим передать

После создания потока мы передаем метод myThread.Start(number); переменную, значение которой хотим передать
в поток.
При использовании ParameterizedThreadStart мы сталкиваемся с ограничением: мы можем запускать во втором потоке только такой метод, который в качестве единственного параметра принимает объект типа object. Поэтому в данном случае нам надо дополнительно привести переданное значение к типу int, чтобы его использовать в вычислениях.

Слайд 25

ПОТОКИ С ПАРАМЕТРАМИ И PARAMETERIZEDTHREADSTART

Но что делать, если нам надо передать не

ПОТОКИ С ПАРАМЕТРАМИ И PARAMETERIZEDTHREADSTART Но что делать, если нам надо передать
один, а несколько параметров различного типа? В этом случае на помощь приходит классовый подход:

Слайд 26

Сначала определяем специальный класс Counter, объект которого будет передаваться во второй поток,

Сначала определяем специальный класс Counter, объект которого будет передаваться во второй поток,
а в методе Main передаем его во второй поток.
Но тут опять же есть одно ограничение: метод Thread.Start не является типобезопасным, то есть мы можем передать в него любой тип, и потом нам придется приводить переданный объект к нужному нам типу.

Слайд 27

ПОТОКИ С ПАРАМЕТРАМИ И PARAMETERIZEDTHREADSTART

Для решения данной проблемы рекомендуется объявлять все используемые

ПОТОКИ С ПАРАМЕТРАМИ И PARAMETERIZEDTHREADSTART Для решения данной проблемы рекомендуется объявлять все
методы и переменные в специальном классе, а в основной программе запускать поток через ThreadStart.
Например:

Слайд 29

СИНХРОНИЗАЦИЯ ПОТОКОВ

Нередко в потоках используются некоторые разделяемые ресурсы, общие для всей программы.

СИНХРОНИЗАЦИЯ ПОТОКОВ Нередко в потоках используются некоторые разделяемые ресурсы, общие для всей
Это могут быть общие переменные, файлы, другие ресурсы.
Например:

Слайд 30

Здесь у нас запускаются пять потоков, которые работают с общей переменной x.

Здесь у нас запускаются пять потоков, которые работают с общей переменной x.
И мы предполагаем, что метод выведет все значения x от 1 до 8. И так для каждого потока. Однако в реальности в процессе работы будет происходить переключение между потоками, и значение переменной x становится непредсказуемым.

Слайд 31

СИНХРОНИЗАЦИЯ ПОТОКОВ

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

СИНХРОНИЗАЦИЯ ПОТОКОВ Решение проблемы состоит в том, чтобы синхронизировать потоки и ограничить
к разделяемым ресурсам на время их использования каким-нибудь потоком. Для этого используется ключевое слово lock. Оператор lock определяет блок кода, внутри которого весь код блокируется и становится недоступным для других потоков до завершения работы текущего потока. И мы можем переделать предыдущий пример следующим образом:

Слайд 32

Для блокировки с ключевым словом lock используется объект-заглушка, в данном случае это

Для блокировки с ключевым словом lock используется объект-заглушка, в данном случае это
переменная locker. Когда выполнение доходит до оператора lock, объект locker блокируется, и на время его блокировки монопольный доступ к блоку кода имеет только один поток. После окончания работы блока кода, объект locker освобождается и становится доступным для других потоков.

Слайд 33

МОНИТОРЫ

Наряду с оператором lock для синхронизации потоков мы можем использовать мониторы, представленные

МОНИТОРЫ Наряду с оператором lock для синхронизации потоков мы можем использовать мониторы,
классом System.Threading.Monitor.
Фактически конструкция оператора lock из прошлого примера инкапсулирует в себе синтаксис использования мониторов. А рассмотренный пример будет эквивалентен следующему коду:

Слайд 34

Метод Monitor.Enter блокирует объект locker так же, как это делает оператор lock.

Метод Monitor.Enter блокирует объект locker так же, как это делает оператор lock.
А в блоке try...finally с помощью метода Monitor.Exit происходит освобождение объекта locker, и он становится доступным для других потоков.

Слайд 35

МОНИТОРЫ

Кроме блокировки и разблокировки объекта класс Monitor имеет еще ряд методов, которые

МОНИТОРЫ Кроме блокировки и разблокировки объекта класс Monitor имеет еще ряд методов,
позволяют управлять синхронизацией потоков. Так, метод Monitor.Wait освобождает блокировку объекта и переводит поток в очередь ожидания объекта. Следующий поток в очереди готовности объекта блокирует данный объект. А все потоки, которые вызвали метод Wait, остаются в очереди ожидания, пока не получат сигнала от метода Monitor.Pulse или Monitor.PulseAll, посланного владельцем блокировки. Если метод Monitor.Pulse отправлен, поток, находящийся во главе очереди ожидания, получает сигнал и блокирует освободившийся объект. Если же метод Monitor.PulseAll отправлен, то все потоки, находящиеся в очереди ожидания, получают сигнал и переходят в очередь готовности, где им снова разрешается получать блокировку объекта.

Слайд 36

КЛАСС AUTORESETEVENT

Класс AutoResetEvent также служит целям синхронизации потоков. Этот класс является оберткой

КЛАСС AUTORESETEVENT Класс AutoResetEvent также служит целям синхронизации потоков. Этот класс является
над объектом ОС Windows "событие" и позволяет переключить данный объект-событие из сигнального в несигнальное состояние. Так, предыдущий пример мы можем переписать с использованием AutoResetEvent следующим образом:

Слайд 37

Во-первых, создаем переменную типа AutoResetEvent. Передавая в конструктор значение true, мы тем

Во-первых, создаем переменную типа AutoResetEvent. Передавая в конструктор значение true, мы тем
самым указываем, что создаваемый объект изначально будет в сигнальном состоянии.
Когда начинает работать поток, то первым делом срабатывает определенный в методе Count вызов waitHandler.WaitOne(). Метод WaitOne указывает, что текущий поток переводится в состояние ожидания, пока объект waitHandler не будет переведен в сигнальное состояние. И так все потоки у нас переводятся в состояние ожидания.

Слайд 38

После завершения работы вызывается метод waitHandler.Set, который уведомляет все ожидающие потоки, что

После завершения работы вызывается метод waitHandler.Set, который уведомляет все ожидающие потоки, что
объект waitHandler снова находится в сигнальном состоянии, и один из потоков "захватывает" данный объект, переводит в несигнальное состояние и выполняет свой код. А остальные потоки снова ожидают. Так как в конструкторе AutoResetEvent мы указываем, что объект изначально находится в сигнальном состоянии, то первый из очереди потоков захватывает данный объект и начинает выполнять свой код.

Слайд 39

КЛАСС AUTORESETEVENT

Но если бы мы написали
AutoResetEvent waitHandler = new AutoResetEvent(false),
тогда

КЛАСС AUTORESETEVENT Но если бы мы написали AutoResetEvent waitHandler = new AutoResetEvent(false),
объект изначально был бы в несигнальном состоянии, а поскольку все потоки блокируются методом waitHandler.WaitOne() до ожидания сигнала, то у нас попросту случилась бы блокировка программы, и программа не выполняла бы никаких действий.
Если у нас в программе используются несколько объектов AutoResetEvent, то мы можем использовать для отслеживания состояния этих объектов методы WaitAll и WaitAny, которые в качестве параметра принимают массив объектов класса WaitHandle - базового класса для AutoResetEvent.
Так, мы тоже можем использовать WaitAll в вышеприведенном примере. Для этого надо строку waitHandler.WaitOne(); заменить на следующую:
AutoResetEvent.WaitAll(new WaitHandle[] {waitHandler});

Слайд 40

МЬЮТЕКСЫ

Еще один инструмент управления синхронизацией потоков представляет класс Mutex, также находящийся в

МЬЮТЕКСЫ Еще один инструмент управления синхронизацией потоков представляет класс Mutex, также находящийся
пространстве имен System.Threading. Данный класс является классом-оболочкой над соответствующим объектом ОС Windows "мьютекс". Перепишем предыдущий пример, используя мьютексы:

Слайд 41

Сначала создаем объект мьютекса: Mutex mutexObj = new Mutex().
Основную работу по синхронизации

Сначала создаем объект мьютекса: Mutex mutexObj = new Mutex(). Основную работу по
выполняют методы WaitOne() и ReleaseMutex(). Метод mutexObj.WaitOne() приостанавливает выполнение потока до тех пор, пока не будет получен мьютекс mutexObj.
После выполнения всех действий, когда мьютекс больше не нужен, поток освобождает его с помощью метода mutexObj.ReleaseMutex()

Слайд 42

Таким образом, когда выполнение дойдет до вызова mutexObj.WaitOne(), поток будет ожидать, пока

Таким образом, когда выполнение дойдет до вызова mutexObj.WaitOne(), поток будет ожидать, пока
не освободится мьютекс. И после его получения продолжит выполнять свою работу.

Слайд 43

МЬЮТЕКСЫ

Мы использовали мьютекс для синхронизации потоков. Однако замечательная черта мьютексов состоит также

МЬЮТЕКСЫ Мы использовали мьютекс для синхронизации потоков. Однако замечательная черта мьютексов состоит
в том, что они могут также применяться не только внутри одного процесса, но и между процессами. Типичный пример - создание приложения, которое можно запустить только один раз. Создадим подобное приложение:

Слайд 44

В данном случае для создания мьютекса мы используем другую перегрузку конструктора. Значение

В данном случае для создания мьютекса мы используем другую перегрузку конструктора. Значение
true, которое передается в качестве первого параметра конструктора, указывает, что приложение будет запрашивать владение мьютексом. Второй параметр указывает на уникальное имя мьютекса. В данном случае в качестве имени выбран guid приложения, то есть глобальный уникальный идентификатор.

Слайд 45

Третий параметр возвращает значение из конструктора. Если он равен true, то это

Третий параметр возвращает значение из конструктора. Если он равен true, то это
означает, что мьютекс запрошен и получен. А если false - то запрос на владение мьютексом отклонен. И после создания мьютекса, если мы запустим вторую копию приложения, то она будет закрыта. И в один момент времени сможет работать только одна копия программы.

Слайд 46

СЕМАФОРЫ

Еще один инструмент, который предлагает нам платформа .NET для управления синхронизацией, представляют

СЕМАФОРЫ Еще один инструмент, который предлагает нам платформа .NET для управления синхронизацией,
семафоры. Семафоры позволяют ограничить доступ определенным количеством объектов.
Например, у нас такая задача: есть некоторое число читателей, которые приходят в библиотеку три раза в день и что-то там читают. И пусть у нас будет ограничение, что единовременно в библиотеке не может находиться больше трех читателей. Данную задачу очень легко решить с помощью семафоров:

Слайд 47

В данной программе читатель представлен классом Reader. Он инкапсулирует всю функциональность, связанную

В данной программе читатель представлен классом Reader. Он инкапсулирует всю функциональность, связанную
с потоками, через переменную Thread myThread.
Для создания семафора используется класс Semaphore:
static Semaphore sem = new Semaphore(3, 3);. Его конструктор принимает два параметра: первый указывает, какому числу объектов изначально будет доступен семафор, а второй параметр указывает, какой максимальное число объектов будет использовать данный семафор. В данном случае у нас только три читателя могут одновременно находиться в библиотеке, поэтому максимальное число равно 3.

Слайд 48

Основной функционал сосредоточен в методе Read, который и выполняется в потоке. В

Основной функционал сосредоточен в методе Read, который и выполняется в потоке. В
начале для ожидания получения семафора используется метод sem.WaitOne(). После того, как в семафоре освободится место, данный поток заполняет свободное место и начинает выполнять все дальнейшие действия. После окончания чтения мы высвобождаем семафор с помощью метода sem.Release(). После этого в семафоре освобождается одно место, которое заполняет другой поток.
А в методе Main нам остается только создать читателей, которые запускают соответствующие потоки.

Слайд 50

ИСПОЛЬЗОВАНИЕ ТАЙМЕРОВ

Одним из важнейших классов, находящихся в пространстве имени System.Threading, является класс

ИСПОЛЬЗОВАНИЕ ТАЙМЕРОВ Одним из важнейших классов, находящихся в пространстве имени System.Threading, является
Timer. Данный класс позволяет запускать определенные действия по истечению некоторого периода времени.
Например, нам надо запускать какой-нибудь метод через каждые 2000 миллисекунд, то есть раз в две секунды:

Слайд 51

Первым делом создается объект делегата TimerCallback, который в качестве параметра принимает метод.

Первым делом создается объект делегата TimerCallback, который в качестве параметра принимает метод.
Причем данный метод должен в качестве параметра принимать объект типа object. И затем создается таймер. Данная перегрузка конструктора таймера принимает четыре параметра: объект делегата TimerCallback; объект, передаваемый в качестве параметра в метод Count; количество миллисекунд, через которое таймер будет запускаться (в данном случае таймер будет запускать немедленно после создания, так как в качестве значения используется 0); интервал между вызовами метода Count. И, таким образом, после запуска программы каждые две секунды будет срабатывать метод Count.