Г. Шилдт - С# 3.0 Полное руководство. 2010 (1160798), страница 163
Текст из файла (страница 163)
Если разрешения ожидает другой поток, то он получает его в этот момент. Количество одновременно разрешаемых доступов указывается при создании семафора. Так, если создать семафор, одновременно разрешающий только один доступ, то такой семафор будет действовать как мьютекс. Семафоры особенно полезны в тех случаях, когда общий ресурс состоит из группы или пула ресурсов. Например, пул ресурсов может состоять из целого ряда сетевых соединений, каждое из которых служит для передачи данных.
Поэтому потоку, которому требуется сетевое соединение, все равно, какое именно соединение он получит. В данном случае семафор обеспечивает удобный механизм управления доступом к сетевым соединениям. Семафор реализуется в классе 5узсев. т)ггеас)1по. 5евар)тоге, у которого имеется несколько конструкторов. Ниже приведена простейшая форма конструктора данного класса. ри)с11с Зеварпоге(апг 1пхг1а1, 1пс вах) где 1п1 Сйа1 — это первоначальное значение для счетчика разрешений семафора, те. количество первоначально доступных разрешений; вал — максимальное значение данного счетчика, т.е.
максимальное количество разрешений, которые может дать семафор. Семафор применяется таким же образом, как и описанный ранее мьютекс. В целях получения доступа к ресурсу в коде программы вызывается метод ИаагОпе () для семафора. Этот метод наследуется классом зевар)тоге от класса Иа1снапг(1е. Метод Ха1ГОпе () ожидает до тех пор, пока не будет получен семафор, для которого он вызывается. Таким образом, он блокирует выполнение вызывающего потока до тех пор, пока указанный семафор не предоставит разрешение на доступ к ресурсу Если коду больше не требуется владеть семафором, он освобождает его, вызывая метод не1еазе () .
Ниже приведены две формы этого метода. рип11с апг Пе1еапе() рив11с 1пг Не1еапе(апс пив) В первой форме метод пе1еазе() высвобождает только одно разрешение, а во второй форме — количество разрешений, определяемых параметром пив. В обеих формах данный метод возвращает подсчитанное количество разрешений, существовавших до высвобождения. Метод НаасОпе () допускается вызывать в потоке несколько раз перед вызовом метода Ие1еазе () . Но количество вызовов метода На1гопе () должно быть равно количеству вызовов метода Ие1еазе () перед высвобождением разрешения. С другой стороны, можно воспользоваться формой вызова метода не1еазе (апс пив), чтобы передать количество высвобождаемых разрешений, равное количеству вызовов метода На1гопе () .
Ниже приведен пример программы, в которой демонстрируется применение семафора. В этой программе семафор используется в классе пут)чгеас) для одновременного выполнения только двух потоков типа Мутвгеаоь Следовательно, разделяемым ресурсом в данном случае является ЦП. 822 Часть П. Библиотека С» // Использовать семафор. пзапд Эузгешт пзапд Эузсеш,ТЬгеабтпдт // Этот поток разрешает одновременное выполнение только // двух своих экземпляров.
с1азз МуТЬгеаб ( рпЬ11с ТЬгеаб ТЬгс(т // Здесь создается семафор, дающий только два // разрешения из двух первоначально имеющихся. всасас ЭешарЬоге зеш = пее 5ешарпоге(2, 2)т рпЬ11с Мутпгеаб(зсг1пд паве) ( ТЬгб = пен ТЬгеаб(СЬТз.апп)т ТЬгб.Маше = паве> ТЬгб.5гагс()т ) // Точка входа в поток. чогб Ппп() ( Сопзо1е.игагеьапе(ТЬгб.наше + " ожидает разрешения."); зеш.иа»попе()т сопзо1е.иг1сеь1пе(тпгб.наше + " получает разрешение."); Еог(спас сЬ='А'т сЬ < '0'т сЬ++) ( сопао1е .Мгасет Тле (тьгб . Маше + " : " + сь + " ") т ТЬгеаб.51еер(500)т ) Сопзо1е.иг1геьапе(ТЬгб.наше + " высвобождает разрешение.")т // Освободить семафор.
зеш.ае1еаае()т ! с1азз 5ешарЬогебешо ( зсас1с чоап ма1п() ( // Сконструировать три потока. ИуТЬгеаб шг1 пен МуТЬгеаб("Поток »1")т ИуТЬгеаб шс2 = пен ИуТЬгеаб("Поток »2")т МуТЬгеаб шГЗ пен МуТЬгеаб("Поток »3")т шс1.тьгб. то1п () т шг2.ТЬгП.Юотп()т шСЗ.ТЬгс(.оо»п()т ! Глава 23.
Многопоточное программирование 823 В классе мут)чгеаб объявляется семафор зелч как показано ниже. ягагъс яешарпоге яеш = пеы яешарпоге (2, 2); При этом создается семафор, способный дать не более двух разрешений на доступ к ресурсу из двух первоначально имеющихся разрешений. Обратите внимание на то, что выполнение метода мутпгеаб.ппп () не может быть продолжено до тех пор, пока семафор зем не даст соответствующее разрешение. Если разрешение отсутствует, то выполнение потока приостанавливается. Когда же разрешение появляется, выполнение потока возобновляется. В методе 1п ма№п () создаются три потока. Но выполняться могут только два первых потока, а третий должен ожидать окончания одного из этих двух потоков.
Ниже приведен результат выполнения рассматриваемой здесь программы, хотя у вас он может оказаться несколько иным. Семафор, созданный в предыдущем примере, известен только тому процессу, который его породил. Но семафор можно создать и таким образом, чтобы он был известен гденибудь еще. Для этого он должен быть именованным. Ниже приведены формы конструктора класса Яешар)чоге, предназначенные для создания такого семафора. рпыьс яешарьоге(ьпг дл1гга1, гпг шах, ягг1пч иыя) риЫ1с Яешарьоге Мпс Плхсха1, 1пс шах, ясг1по иыя, опг Ьоо1 произошедшее) В обеих формах иыя обозначает конкретное имя,передаваемое конструктору.
Если в первой форме семафор, на который указывает имя, еще не существуег, то он создается с помощью значений, определяемых параметрами упзгуа1 и шах. А если он уже существуег, то значения параметров 1пу Сда1 и шах игнорируются. После возврата из второй формы конструктора параметр произошедшее будет иметь логическое значение ггпе, если семафор был создан. В этом случае значения параметров ьп1г1а1 и шах используются для создания семафора. Если же параметр произошедшее будет иметь логическое значение №а1зе, семафор уже существует и значения параметров гп№Г1а1 и шах игнорируются.
Существует и третья форма конструктора класса Яешар)(оге, в которой допускается указывать управляющий доступом объект типа яешарпогеяеспг№гу. С помощью именованных семафоров можно синхронизировать взаимодействие процессов. Поток Поток Поток Поток Поток Поток поток Поток поток Поток Поток Поток Поток Поток Поток Поток Поток Поток №1 ожидает разрешения.
№1 получает разрешение. №1: й №2 ожидает разрешения. №2 получает разрешение. №2: й №3 ожидает разрешения. №1: В №2: В №1: С №2: С №1 высвобождает разрешение. №3 получает разрешение. №3: й №2 высвобождает разрешение. №3: В №3 №3 высвобождает разрешение. 824 Часть й. Библиотека С№ Применение событий Для синхронизации в С№ предусмотрен еще один тип объекта: событие.
Существуют две разновидности событий: устанавливаемые в исходное состояние вручную и автоматически. Они поддерживаются в классах маппа1ее яе сечен с и Аос бее яе сече ос соответственно. Эти классы являются производными от класса Ечепгнайгиапб1е, находящегося на верхнем уровне иерархии классов, и применяются в тех случаях, когда один поток ожидает появления некоторого события в другом потоке. Как только такое событие появляется, второй поток уведомляет о нем первый поток, позволяя тем самым возобновить его выполнение. Ниже приведены конструкторы классов Маппа1КеяеСЕчепт и АптопеяеСЕчепт. роЬ11с Мапоа1певескчепс (Ьоо1 состояние) роЬ11с Аокопеяекячепк(Ьоо1 состояние) Если в обеих формах параметр состояние имеет логическое значение сгое, то первоначально уведомляется о событии. А если он имеет логическое значение Га1яе, то о событии первоначально не уведомляется.
Применяются события очень просто. Так, для события типа Мапоа1пеяесЕчепс порядок применения следующий. Поток, ожидающий некоторое событие, вызывает метод иаъсОпе () для событийного объекта, представляющего данное событие. Если событийный объект находится в сигнальном состоянии, то происходит немедленный возврат из метоДа ИадСОпе () .
В противном случае выполнение вызывающего потока приостанавливается до тех пор, пока не будет получено уведомление о событии. Как только событие произойдет в другом потоке, этот поток установит событийный объект в сигнальное состояние, вызвав метод еес () . Поэтому метод еес () следует рассматривать как уведомляющий о том, что событие произошло. После установки событийного объекта в сигнальное состояние произойдет немедленный возврат из метода иа1сОпе (), и первый поток возобновит свое выполнение. А в результате вызова метода невес () событийный объект возвращается в несигнальное состояние. Событие типа Апсоееяесечепс отличается от события типа мапоа1ееяесечепс лишь способом установки в исходное состояние.
Если для события типа маппа1пеяесЕчепс событийный объект остается в сигнальном состоянии до тех пор, пока не будет вызван метод Ееяес(), то для события типа АпсопеяесЕчепс событийный объект автоматически переходит в несигнальное состояние, как только поток, ожидающий это событие, получит уведомление о нем и возобновит свое выполнение. Поэтому если применяется событие типа АокопеяеСЕчепт, то вызывать метод Ееяег () необязательно. В приведенном ниже примере программы демонстрируется применение события типа Мапиа1пеяеСЕчепк.