Г. Шилдт - С#4.0 Полное руководство (1160795), страница 173
Текст из файла (страница 173)
Ниже приведена его простейшая форма. риЫгс Ьсс1 Хатсбпе (); Метод паз СОпе () ожидает до тех пор, пока не будет получен мьютекс, для которого он был вызван. Следовательно, этот метод блокирует выполнение вызывающего потока до тех пор, пока не станет доступным указанный мьютекс. Он всегда возвращает логическое значение Ьгие. Когда же в коде больше не требуется владеть мьютексом, он освобождается посредством вызова метода ке1еааемигех (), форма которого приведена ниже. риЫгс чсШ Пе1еааеникех О В этой форме метод Пе1еааемисех () освобождает мьютекс, для которого он был вызван, что дает возможность другому потоку получить данный мьютекс.
Для применения мьютекса с целью синхронизировать доступ к общему ресурсу упомянутые выше методы хаьсОле () и пе1еааемиьех () используются так, как показано в приведенном ниже фрагменте кода. Мисех вуМСх = пем Михея О вумсх.натьопе О; О схнлать получения мьютекса Получить доступ к сбвему ресурсу. вумсх.пе1еааемисех О; // освободить мьютекс 864 Часть 11. Библиотека С№ При вызове метода ХаТСОпе () выполнение соответствующего потока приостанавливается до тех пор, пока не будет получен мыотекс. А при вызове метода Ке1еавемигех () мьютекс освобождается и затем может быть получен другим потоком.
Благодаря такому подходу к синхронизации одновременный доступ к общему ресурсу ограничивается только одним потоком. В приведенном ниже примере программы описанный выше механизм синхронизации демонстрируется на практике. В этой программе создаются два потока в виде классов 1псТЛгеаб и ПесТЛгеаб, которым требуется доступ к общему ресурсу: переменной БЛагебнев. Соипг. В потоке 1псТЛгеаб переменная ЯЛагебнев.СоипС инкрементируется, а в потоке РесТЛгеаб — декрементируется.
Во избежание одновременного доступа обоих потоков к общему ресурсу Бпагебнев . Соипс этот доступ синхронизируется мьютексом Игх, также являющимся членом класса БЛагебнев. Применить мьютекс. изгиб Бувсев; овсов Бувгев.ТЛгеабьпо; // В этом классе содержится общий ресурс(переменная Соипг), // а также мьютекс (Мгх), управляющий доступом к ней. с1авв Бпагебпев ( риЫ1с вгаС1с 1пг СоипС = 0; риЫьс вгагсс Мигех Мгх = пен Мигех(); ) В этом потоке переменная Япагебнев.соипс инкрементируется.
с1авв 1псТЛгеаб ( 1пс. пив; риЫсс Тпгеаб ТЛгб; риЫьс 1псТЛгеаб(вггьпЧ пате, ьпг и) ( тлгб = пен тлгеаб(сыв.аип); пвп=п; Тпгб.нате = пате; тпгб.ЯСагг(); Точка входа в поток. чогб Вип() Сопво1е .Хг1сеьвпе (Тпгб.Нате + " ожидает мьютекс.") О Получить мьютекс. Япагебпев.нгх.иасгбпе(); Сопво1е.игсгевгпе(ТЛгб.нате + " получает мьютекс.") бо ( Тпгеаб.Б1еер(500); япагебнев.соипс+ьг Сопво1е.игггеьгпе("В потоке " ь Тпгб.Нате + Япагебнев.соипг = " + Бпагебдев.Соипг) пив —; ) нЫ1е(пив > 0) Гиена 23. Многопоточное программирование.
Часть первая: основы 868 Сопяо1е.иг11еь1ве(ТВгб.нате ь " освобождает мьютекс.") Освободить мьютекс. Ббагебпея.мох.пе1еаяенпвех()г ) О В этом потоке переменная Бвагебкея.сопля декрементируется. с1аяя ОесТВгеаб ( 1вг ппт; рвб11с Тьгеаб Тьгбг рв)>11с Оествгеаб(ягггпо птпе, гпг и) ( Тбгб = пен Тбгеаб(пен Тьгеабвсагя(ГВ1я.поп))) ппт = и; Тьгб.нате = вате; Тьгб.БГагс()1 ) // Точка входа в поток. уогб Кпп() ( совяо1е.хгггеьбпе(тьгб.мате + " ожидает мьютекс."); О Получить мьютекс. Бвагебяея.МГх.Ха1СОпе() Сопяо1е.нг1яеь1ве(ТВгб.нате ь " получает мьютекс.")г бо ( Тьгеаб.Б1еер(500); ЯВагебдея.Сопля †; Сопво1е.Хггсеьбпе("В потоке " + Тьгб.нате + Бьагебпея.сопля = " + Бьагебяея.Соппг)Г ппт —; ньг'е (ппт > О) г сопяо1е.хгггеп1пе(твгб.мате + " освобождает мьютекс.") // Освободить мьютекс.
Ббагебяея.нсх.яе1еаяеМпсех(); ) ) с1аяя Мпбехбето ( ясасгс чобб Магп() ( О Сконструировать два потока. 1псТпгеаб тГ1 = пен 1псТВгеаб("Инкрементирующий Поток", 5); Тпгеаб.51еер(1); У/ разрешить инкрементируггцему потоку начаться ОесТВгеаб тГ2 = пен ОесТВгеаб("Декрементирукший Поток", 5); п|Г1 . Тнгб. ТО1п (); Вбб Часть ((. Библиотека б№ шг2.ТЬгб.Зо1п()с ) Эта программа дает следующий результат.
Инкрементируюший Поток ожидает мьютекс. Инкрементирующий Поток получает мьютекс. Декрементирукявсй Поток ожидает мьютекс. В потоке Инкрементирукший Поток, ЯЬагебЫев.сопля В потоке Инкрементируюций Поток, ЯЬвгебнев.сопля В потоке Инкрементируюций Поток, БЬагебнев.соппц В потоке Инкрементирующий Поток, ЯЬагебнев.сопля В потоке Инкрементирукиций Поток, ЯЬагесЫев.сопля Инкрементирухжий Поток освобождает мьютекс. Декрементирукаий Поток получает мьютекс.
В потоке Декрементирукщий Поток, БЬагебРев.сопля В потоке Декрементируюший Поток, Бпагебнев.Соопг В потоке Декрементируспций поток, япвгебнев.сопля В потоке Декрементируюций Поток, ЯЬагебнев.сопля В потоке Декрементируюший Поток, БЬагебнев.соппс Декрементирующий Поток освобождает мьютекс. 1 = 2 3 4 5 — 4 — 3 — 2 1 О Как следует из приведенного выше результата, досгуп к общему ресурсу (переменной БЬагебнея.
Соппс) синхронизирован, и поэтому значение данной переменной может быль одновременно изменено только в одном потоке. Для того чтобы убедиться в том, что мьютекс необходим для получения приведенного выше результата, попробуйте закомментировать вызовы методов Иаьсспе () и Пе1еаяемпсех () в исходном коде рассматриваемой здесь программы. При ее последующем выполнении вы получите следующий результат, хотя у вас он может оказаться несколько иным.
В потоке В потоке В потоке В потоке В потоке В потоке В потоке В потоке В потоке Как следует из приведенного выше результата, без мьютекса инкрементирование и декрементирование переменной БЬагебнея. Соппс происходит, скорее, беспорядочно, чем последовательно. Мьютекс, созданный в предыдущем примере, известен только тому процессу, который его породил. Но мьютекс можно создать и таким образом, чтобы он был известен где-нибудь еще. Для этого он должен быть именованным.
Ниже приведены формы конструктора, предназначенные для создания такого мьютекса. рпЬ11с Мпгех(Ьоо1 дп1сга11уоьпеб, яцгьпо имв) рпЬ1гс Мацек(Ьоо1 зпбвга11уомпеб, всг1по имя, опс Ьоо1 сгеацебь)ем) В обеих формах конструктора имя обозначает конкретное имя мьютекса. Если в первой форме конструктора параметр 1пб Сба11уОмпес( имеет логическое значение Инкрементируюший Поток, Декрементируюций Поток, Инкрементиру~сщий Поток, Декрементируюций Поток, Инкрементируюций Поток, Декрементируюший Поток, Инкрементиру~сщий Поток, Декрементирующий Поток, Инкрементиру~сший Поток, ЯЬагебнев.соопц ЯЬагебнев.сопля Япагебнев.сопля БЬагебнев.Сопля БЬагебхев.соопц ЯЬагесЫев.сопля ЯЬагебнев.сопля БЬагебнев.Соопц Япагебнев.соипг 1 — Π— 1 О = 1 О 1 О 1 Глава 23. Многопоточное программирование.
Часть первая: основы 867 г гпе, то владение мьютексом запрашивается. Но поскольку мьютекс может принадлежать другому процессу на системном уровне, то для этого параметра лучше указать логическое значение га1зе. А после возврата из второй формы конструктора параметр скеа пес(л)еи будет иметь логическое значение стае, если владение мьютексом было запрошено и получено, и логическое значение г а1зе, если запрос на владение был отклонен.
Существует и третья форма конструктора типа ипсех, в которой допускается указывать управляющий доступом объект типа Ип Ге хБ есп гйг у, С помощью именованных мьютексов можно синхронизировать взаимодействие процессов. И последнее замечание: в потоке, получившем мьютекс, допускается делать один или несколько дополнительных вызовов метода Ха1сОпе () перед вызовом метода ке1еазеиы бек (), причем все эти дополнительные вызовы будут произведены успешно. Это означает, что дополнительные вызовы метода иайг опе () не будут блокировать поток, который уже владеет мьютексом. Но количество вызовов метода Иа1СОпе () должно быть равно количеству вызовов метода йе1еазеыосех () перед освобождением мьютекса.
Семафор Семафор подобен мьютексу, за исключением того, что он предоставляет одновременный доступ к общему ресурсу не одному, а нескольким потокам. Поэтому семафор пригоден для синхронизации целого ряда ресурсов. Семафор управляет доступом к общему ресурсу, используя для этой цели счетчик. Если значение счетчика больше нуля, то доступ к ресурсу разрешен. А если это значение равно нулю, то доступ к ресурсу запрещен. С помощью счетчика ведется подсчет количества разрешений.
Следовательно, для доступа к ресурсу поток должен получить разрешение от семафора. Обычно поток, которому требуется доступ к общему ресурсу, пытается получить разрешение от семафора. Если значение счетчика семафора больше нуля, то поток получает разрешение, а счетчик семафора декрементируется. В противном случае поток блокируется до тех пор, пока не получит разрешение. Когда же потоку больше не требуется доступ к общему ресурсу, он высвобождает разрешение, а счетчик семафора инкрементируется.
Если разрешения ожидает другой поток, то он получает его в этот момент. Количество одновременно разрешаемых доступов указывается при создании семафора. Так, если создать семафор, одновременно разрешающий только один доступ, то такой семафор будет действовать как мьютекс. Семафоры особенно полезны в тех случаях, когда общий ресурс состоит из группы или пула ресурсов. Например, пул ресурсов может состоять из целого ряда сетевых соединений, каждое из которых служит для передачи данных. Поэтому потоку, которому требуется сетевое соединение, все равно, какое именно соединение он получит. В данном случае семафор обеспечивает удобный механизм управления доступом к сетевым соединениям. Семафор реализуется в классе Б уз сепг. Т)з геаг)1пгг.