Г. Шилдт - С# 3.0 Полное руководство. 2010 (1160798), страница 162
Текст из файла (страница 162)
Зоьп () г Сопэо1е.игъееввпе (" Часы остановлены"); ) Эта версия программы дает такой же результат, как )( предыдущая. Синхронизируемый метод не определен в открытом классе и не вызывается для открытого объекта, поэтому применение оператора 1осх или атрибута Меепос(1вр1АСЬг1Ьпее зависит от личных предпочтений, так как и тот и другой дает один и тот же результат. Но поскольку ключевое слово 1ос)к относится непосредственно к языку СФ, то в примерах, приведенных в этой книге, предпочтение отдано именно ему. На заметку! Не применяйте атрибут ме сл от) тыр1л с к гъь и се в открытых классах или экземплярах открытых обьектое. Вместо этого пользуйтесь оператором 1ос К, чтобы заблокировать метпод для закрытого обьекта, как пояснялось ранее. Применение мьютекса и семафора В большинстве случаев, когда требуется синхронизация, оказывается достаточно и оператора 1ос)к. Тем не менее в некоторых случаях, как, например, при ограничении доступа к общим ресурсам, более удобными оказываются механизмы синхронизации, встроенные в среду .)к)ЕТ ггашетчог)к.
Ниже рассматриваются по порядку два таких механизма: мьютекс и семафор. Мьютекс Мьютаекс представляет собой взаимно исключающий синхронизирующий объект. Это означает, что он может быть получен потоком только по очереди. Мьютекс предназначен для тех ситуаций, в которых общий ресурс может быть одновременно использован только в одном потоке. Допустим, что системный журнал совместно используется в нескольких процессах, но только в одном из них данные могут записываться в файл этого журнала в любой момент времени.
Для синхронизации процессов в данной ситуации идеально подходит мьютекс. Мьютекс поддерживается в классе Яузсеы. Тпгеас(1по. Мпгех. У него имеется несколько конструкторов. Ниже приведены два наиболее употребительных конструктора. рпЬ11с Мпкех 0 рпвъьс Мпкех(Ьоо1 владение) В первой форме конструктора создается мьютекс, которым первоначально никто не владеет. А во второй форме исходным состоянием мьютекса завладевает вызывающий поток, если параметр владение имеет логическое значение сгпе.
В противном случае мьютексом никто не владеет. Для того чтобы получить мьютекс, в коде программы следует вызвать метод иайс0пе () для этого мьютекса. Метод Ха1СОпе () наследуется классом Мпгех от класса Тпгеас). Иайгнапб1е. Ниже приведена его простейшая форма. рпв11с Ьоо1 ИаьСОпе()) Глава 23. Многопоточное программирование 817 Метод Иа1Сопе () ожидает до тех пор, пока не будет получен мьютекс, для которого он был вызван. Следовательно, этот метод блокирует выполнение вызывающего потока до тех пор, пока не станет доступным указанный мьютекс. Он всегда возвращает логическое значение ггпе. Когда же в коде больше не требуется владеть мьютексом, он освобождается посредством вызова метода Ве1еаэемпсех (), форма которого приведена ниже.
рнваас чов б Ке1еавеингек() В этой форме метод Не1еаэемпоех () освобождает мьютекс, для которого он был вызван, что дает возможность другому потоку получить данный мьютекс. Для применения мьютекса с целью синхронизировать доступ к общему ресурсу используются методы ха110пе () и не1еаэемпгех (), как показано в приведенном ниже фрагменте кода. Мисех щуМГх = пен Мосек() щумгх.иа1сбпе()г // ожидать получения мьютекса // Получить доступ к общему ресурсу. щумсх.ке1еаэемигех()г // освободить мьютекс При вызове метода на1гбпе () выполнение соответствующего потока приостанавливается до тех пор, пока не будет получен мьютекс. А при вызове метода Не1еа эемп ге х ( ) мьютекс освобождается и затем может быть получен другим потоком. Благодаря такому подходу к синхронизации одновременный доступ к общему ресурсу ограничивается только одним потоком.
В приведенном ниже примере программы описанный выше механизм синхронизации демонстрируется на практике. В этой программе создаются два потока в виде классов 1псТЬгеаб и ПесТЬгеас), которым требуется доступ к общему ресурсу: переменной ЯЬагебйеэ.Соппс. В потоке 1псТЬгеаб переменная ЯЬагебнез.Соппс инкрементируется, а в потоке Пестьгеаб — декрементируется.
Во избежание одновременного доступа обоих потоков к общему ресурсу Вьагег(неэ. соппг этот доступ синхронизируется мьютексом Мсх, также являющимся членом класса ВЬагебнеэ. // Применить мьютекс. паапа зувгеан наапд Зуэсещ.тпгеаб1пдг // В этом классе содержится общий ресурс // (переменная Соипс), а также мьютекс (Мсх) // управляющий доступом к ней. с1аээ ЗЬагебдеэ ( развале эсаг1с Тпг Соипс = ог риЬ11с эсаг1с Мнгех Мок = пен Мисех()г ) // В этом потоке переменная ЭЬагебяеэ.Сонно инкрементируется.
818 Часть П. Библиотека С» с1авя 1псТ)кгеаб ( гпс пшпт рпЫ1с Т)тгеаб Т)тгбт рпЫТс тпстпгеаб(ясг1пд паве, 1пг и) ( Тпгб = лен Тпгеаб(с)т1я.йпп)т пою = и; Т)тгб.наюе = ламет Тнгб.ясагг()т ) // Точка входа в поток. тот б Поп () ( Сопяо1е.игбсеВТпе(ТЛгб.наюе + " ожидает мьютекс.")т // Получить мьютекс.
Япагебйея.МСх.иа1СОпе() Сопяо1е.иг1сеЬгпе(ТПгб.наюе + " получает мьютекс.")) бо ( т)тгеаб.51еер(500)) 5пагебйея.Сопля++) Сопяо1е.иг1се11пе("В потоке " + т)тгб.иаюе + Янагебйев.Соопг = " + 5)тагебРея.Соопс)т ппю —; ) нй11е (пою > О) Сопяо1е.игаге11пе(ТЬгб.иаюе + " освобождает мьютекс.")т // Освободить мьютекс. 5)татебна.МГх.не1еаяеМогех() ) // В этом потоке переменная Янагебиея.Сопля декрементируетсв. с1аяя Оестлгеаб ( Тпс поют риЫТс Т)тгеаб Тнгбт роЫ1с Оест)тгеаб(ясг1пд пате, 1пг и) ( Т)тгб = пен Т)тгеаб(пен Т)тгеабягагг(ГП1я.анп)) ппю = пт Тдгб.наюе = ламет Тнгб.ягагс()т ) // Точка входа в поток.
чо1б Кпп() Сопяо1е.иг1сеПТпе(Тйгб.наюе ь " ожидает мьютекс.")т // Получить мьютекс. 820 Часть П. Библиотека Са Как следует из приведенного выше результата, доступ к общему ресурсу (переменной ЯЬагет(вез. Соппс) сиихроиизироваи, и поэтому значение даииой переменной может быть одновременно изменено только в одном потоке. Для того чтобы убедиться в необходимости мьютекса для получения приведенного выше результата, попробуйте закоммеитировать вызовы методов иа1сопе() и Ве1еазеМисех () в исходном коде рассматриваемой здесь программы. При ее последующем выполнении вы получите следующий результат, хотя у вас ои может оказаться несколько иным: Инкрементируюций Поток, декрементирукцций Поток, Инкрементируюший Поток, декрементируюший Поток, Инкрементируююий Поток, Декрементируюций Поток, Инкрементируюший Поток, Декрементируююий Поток, Инкрементируюший Поток, Яьагесвез.Сопок Яьагеоаез.Соппс Япагееаез.Соппс ЯЬагесвез.Сопок Япагееиез.СоипС яьагеоаеа.соппг Япагеааез.сопок Япагеоаез.соипс Япагеааез.соипс = 1 = О = 1 = О .= 1 = О = 1 = О = 1 В потоке В потоке в потоке В потоке В потоке В потоке В потоке В потоке В потоке Как следует из приведенного выше результата, без мьютекса иикремеитироваиие и декремеитироваиие переменной яйагес)поз .Сопок происходит, скорее, рассеянно, чем последовательно.
Мьютекс, созданный в предыдущем примере, известен только тому процессу, который его породил. Но мьютекс можно создать и таким образом, чтобы ои был известен гденибудь еще. Для этого ои должен быть именованным. Ниже приведены формы конструктора, предназначенные для создания такого мьютекса. рпЬ11с Мосек(Ьоо1 владение, згггпд имя) рпваас Мисех(Ьоо1 владение, зсггпд имя, опт Ьоо1 произошедшее) Семафор Семафор подобен мьютексу, за исключением того, что ои предоставляет одиовремеииый доступ к общему ресурсу ие одному, а нескольким потокам.
Поэтому семафор пригоден для синхронизации целого ряда ресурсов. Семафор управляет доступом к общему В обеих формах конструктора имя обозначает конкретное имя мьютекса. Если в первой форме конструктора параметр владение имеет логическое значение сгпе, то владение мьютексом запрашивается. Но поскольку мьютекс может принадлежать другому процессу иа системном уровне, то для этого параметра лучше указать логическое зиачеиие га1зе. А после возврата из второй формы конструктора параметр произошедшее будет иметь логическое значение сгпе, если владение мьютексом было запрошено и получено, и логическое значение га1зе, если запрос иа владение был отклонен.
Существуег и третья форма конструктора типа Мпсех, в которой допускается указывать управляющий доступом объект типа Мпсехзеспг1су. С помощью именованных мьютексов можно синхронизировать взаимодействие процессов. И последнее замечание: в потоке, получившем мьютекс, допускается делать один или более дополнительных вызовов метода иа1ЬОпе () перед вызовом метода Ве1еаземигех П, причем все эти дополнительные вызовы будут произведены успешно. Это означает, что дополнительные вызовы метода Иаасппе () ие будут блокировать поток, который уже владеет мьютексом.
Но количество вызовов метода Иааг()пе () должно быть равно количеству вызовов метода Пе1еазеМпсех () перед освобождением мьютекса. Глава 23. Многопоточное программирование 821 ресурсу, используя для этой цели счетчик. Если значение счетчика больше нуля, то доступ к ресурсу разрешен. А если это значение равно нулю, то доступ к ресурсу запрещен.
С помощью счетчика ведется подсчет количество разрешений. Следовательно, для доступа к ресурсу поток должен получить разрешение от семафора. Обычно поток, которому требуется доступ к обтцему ресурсу, пытается получить разрешение от семафора. Если значение счетчика семафора больше нуля, то поток получает разрешение, а счетчик семафора декрементируется. В противном случае поток блокируется до тех пор, пока не получит разрешение. Когда же потоку больше не требуется доступ к общему ресурсу, он высвобождает разрешение, а счетчик семафора инкрементируется.