Г. Шилдт - С# 3.0 Полное руководство. 2010 (1160798), страница 160
Текст из файла (страница 160)
риЬ11с Мутьгеаб(ясг1по паве, апс[] пива) ( а = пива) ТЬгб = пен Тьгеаб(сьгз.нип); Тпгб.Мазе = паве; Тпгб.эсагс()г // начать поток ) // Начать выполнение нового потока. чогб Нип() ( Сопяо1е.мг1се11пе(тпгб.каше ч " начат."); // Заблокировать вызовы метода Яив1Г(). 1оск(яа) апзнег = яа.Яив1Г(а); Сопяо1е.мг1сеъ1пе("Сумма для потока " + ТЬгб.наше + " равна " + апянег)г Сопяо1е.мгтсеъ1пе(ТЬгб.наше + " завершен."); 808 Часть (!. Библиотека С№ с1аяя Яулс ( якакьс ногк[ Ма№л () ( Ток[) а = (1, 2, 3, 4, 5)т МуТЬкеаб вк1 = пеи МуТЬкеаб("Потомок №1", а)т МуТЬгеаб вк2 " пеи МуТЬгеаб("Потомок №2", а)т вк1.ТЬкб.йо1л()т вк2.ТЬкб.эо1п()т ) ) В данной программе блокируется вызов метода я а.
Яив1с ( ), а не сам метод Яив1 с () . Ниже приведена соответствующая строка кода, в которой осуществляется подобная блокировка. 22 Заблокировать вмяавм метала явить() 1оск(яа) аляиег = яа.эив1С(а)т Объект яа является закрытым, и поэтому он может быть благополучно заблокирован. При таком подходе к синхронизации потоков данная программа дает такой же правильный результат, как и при первоначальном подходе. Класс мот1л.~ое и блокировка Ключевое слово 1ос)к на самом деле служит в С№ сокращенным путем доступа к средствам синхронизации, определенным в классе мопъсог, который находится в пространстве имен Яуягев. ТЬгеас[1по.
В этом классе определен, в частности, ряд методов для управления синхронизацией. Например, для получения блокировки объекта вызывается метод Епгег О, а для снятия блокировки — метод Ехъс () . Ниже приведены общие формы этих методов. риЬ11с якак1с нокб Елкег (оЬ№еск яупсОЬ) риьъъс ясясьс нова ехъс(оьбесс яупсоы где яупсоь обозначает синхронизируемый объект. Если же объект недоступен, то после вызова метода епсег () вызывающий поток ожидает до тех пор, пока объект не станет доступным. Тем не менее методы Епгег () и Ехъг () применяются редко, поскольку оператор 1ос)с автоматически предоставляет эквивалентные средства синхронизации потоков.
Именно поэтому оператор 1осх оказывается более предпочтительным для получения блокировки объекта при программировании на С№. Впрочем, один метод из класса Мопугог может все же оказаться полезным. Это метод тгуепсег (), одна из общих форм которого приведена ниже. рив11с якактс Ьоо1 ТкуЕокег(оЬ№еск яулсОЬ) Этот метод возвращает логическое значение сгпе, если вызывающий поток получает блокировку для объекта яупсОЬ, в противном случае он возвращает логическое значение Та1яе. Но в любом случае вызывающему потоку придется ждать своей очереди. С помощью метода ТгуЕпгег () можно реализовать альтернативный вариант синхронизации потоков, если требуемый объект временно недоступен.
Глава 2Э. Многопоточное программирование 809 Кроме того, в классе ыоп1сог определены методы Иа1с (), Рп1яе () и Рп1яел11 (), которые рассматриваются в следующем разделе. Сообщение между потоками с помощью методов %ахи (), Ра1ве () и Ри18еА11 () Рассмотрим следующую ситуацию. Поток т выполняется в кодовом блоке 1осх, и ему требуется доступ к ресурсу Е, который временно недоступен. Что же тогда делать потоку т? Если поток т войдет в организованный в той или иной форме цикл опроса, ожидая освобождения ресурса н, то тем самым он свяжет соответствующий объект, блокируя доступ к нему других потоков.
Это далеко не самое оптимальное решение, поскольку оно лишает отчасти преимуществ программиронания для многопоточной среды. Более совершенное решение заключается в том, чтобы временно освободить объект и тем самым дать возможность выполняться другим потокам. Такой подход основывается на некоторой форме сообщения между потоками, благодаря которому один поток может уведомлять другой о том, что он заблокирован и что другой поток может возобновить свое выполнение. Сообщение между потоками организуется в С(г счгомощью методов иать (), Рп1яе () и Рп1яе)(11 () . Методы Иате О, Ри1яе() и Рп1яе)(11() определены в классе Иоптеог и могут вызываться только из заблокированного фрагмента блока.
Они применяются следующим образом. Когда выполнение потока временно заблокировано, он вызывает метод Иа1С () . В итоге поток переходит в состояние ожидания, а блокировка с соответствующего объекта снимается, что дает возможность использовать этот объект в другом потоке. В дальнейшем ожидающий поток активизируется, когда другой поток войдет в аналогичное состояние блокировки, и вызывает метод Ри1яе () или Рп1яе)(11 () . При вызове метода Рп1яе () возобновляется выполнение первого потока, ожидающего своей очереди на получение блокировки.
А вызов метода Ри1яед11 () сигнализирует о снятии блокировки всем ожидающим потокам. Ниже приведены две наиболее употребительные формы метода Ха11 () . риЬ11с яеаетс Ьоо1 Ха1С(оЬ)есе иатСОЬ) рпЫтс ясастс Ьоо1 Хаас(оЬ)есв натсОЬ, 1пс миллисекунды) В первой форме ожидание длится вплоть до уведомления об освобождении объекта, а во второй форме — как до уведомления об освобождении объекта, так и до истечения периода времени, на который указывают миллисекунды В обеих формах ыаРЬОЬ обозначает объект, освобождение которого ожидается. Ниже приведены общие формы методов Ри1яе () и Рп1яед11 () . реЫ1с яеаетс чо1г) Ри1яе(оЬ)есе еатСОЫ риЬ11с яеае1с чопз Рп1яел11(оЬЧесС иатСОЬ) где ха1ьоь обозначает освобождаемый объект.
Если методы Хате (), Рп1яе () и Рп1яе))11 () вызываются из кода, находящегося за пределами синхронизированного кода, например блока 1осх, то генерируется исключение Яупспгоп1яае1опЕосХЕхсере1оп. Пример применения методов Иа~~ () и Ри1зе () Для того чтобы стало понятнее назначение методов иате () и Рп1яе (), рассмотрим пример программы, имитирующей тиканье часов и отображающей этот процесс на экра- В10 Часть (), Библиотека С» не словами "тик" и "так". Для этой цели в программе создается класс Т1сКТосК, содержащий два следующих метода: Т1сК () и Тос« () . Метод Т»с« () выводит на экран слово "тик", а метод Тос)с () — слово "так". Для запуска часов далее в программе создаются два потока: один из них вызывает метод ТгсК (), а другой — метод Тос« () .
Преследуемая в данном случае цель состоит в том, чтобы оба потока выполнялись, поочередно выводя на экран слова "тик" и "так", из которых образуется повторяющийся ряд "тик-так", имитирующий ход часов. // Использовать методы Иагг() и Рц1зе() для имитации тиканья часов. цз1пд Яувгеьо цзгпд ЯувСеш.ТЬгеабьпон с1азз ТгсКТос« ( оЬ»есг 1осКОп пен оЬ»есг()) рцЫгс чо16 тгс«(Ьоо1 гцпп1пд) ( 1осК(1осКОп) ( 11()гцпп1пд) ( // остановить часы Мопггог.рц1зе(1осКОп)т // уведомить любые ожидающие потоки гегцгпт ) Сопво1е.нггге("тик ")т моп»сог.ри1зе(1ос«оп)) // разрешить выполнение метода Тос«() Моп1гог.иаьс(1ос«оп)т // ожидать завершения // метода Тос«() рцЫ1с чогб Тос«(Ьоо1 гцпптпд) ( 1ос«(1ос«оп) ( 1Т((гцппгпд) ( // остановить часы Мопгоог.рц1ае(1осКОп)т // уведоыить любые ожидающие потоки гесцгпт ) Сопво1е.нгсгеьспе("так")т Мопггог.Рц1зе(1ос«оп)т // разрешить выполнение метода Тгс«() мопс«ос.насг(1осКОп)) // ожидать завершения метода тгсК() ) ) с1аьз МуТЬгеаб ( рцЫТс Тпгеаб Тнгбт Тгс«ТосК ССОЬт // Сконструировать новый поток.
рцЫгс МуТЬгеаб(згггпд паше, Т1с«Тос« СС) ( ТЬгб = пеи ТЬгеаб(сЫз.ацп)т ССОЬ Сгт Тпгб.Наше = паше) Глава 23, Многопоточное программирование 811 тпгб.згагг О; ) // Начать выполнение нового потока. чо).с( Нпп () ( гт(тпгб.нане == "Ттск") ( тог(тпг 1=0; 1<5н 1++) ггоь.ттск(ггпе) ггОЬ.ттск(га1ве)) ) е1ве ( тот(тпг 1=0; 1<5) 1++) ггоь.тесн(ггое)) ггОЬ.Тоск(га1ве)) ) ) с1ава Ттсг1пЧС1оск ( вгагтс чотб Матп() ( ТтсНТоса гг = пен ТтсНТоск(); Мутпгеаб щг1 = пен Мутпгеаб("Т1сн", гг); Мутпгеаб щг2 = пен Мутпгеаб("Тоск", гг)) щг1. тьго. )01п () н щг2.тпгб.эо1п(); Сопво1е.иг1геЬ1пе("Часы остановлены" ); ) Ниже приведен результат выполнения этой программы.
тик так тик так тик так тик так тик так Часы остановлены Рассмотрим эту программу более подробно. В методе ма1п () создается объект.гг типа Т1с)гтоск, который используется для запуска двух потоков на выполнение. Если в методе поп () из класса мутьгеаг( обнаруживается имя потока т1ск, соответствующее ходу часов "тик", то вызывается метод т1сн () . А если это имя потока тоск, соответствующее ходу часов "так", то вызывается метод Тос(< () . Каждый из этих методов вызывается пять раз подряд с передачей логического значения ггпе в качестве аргумента. Часы идут до тех пор, пока этим методам передается логическое значение ггое, и останавливаются, как только передается логическое значение га1ве.
Самая важная часть рассматриваемой здесь программы находится в методах Т1с)< () и тоск () . Начнем с метода Т1ск (), код которого для удобства приводится ниже. риптгс чогб Ттск(Ьоо1 гппптпд) ( 1осн(1оскоп) ( гт((гппп1пч) ( // остановить часы Моп1гог.эп1ве(1осгоп); // уведомить любые ожидающие потоки 812 Часть (!. Библиотека С() геоогп; ) Сопво1е.иг1ве("тик "); мопъсог.ро1ве(1оскоп); // разрешить выполнение метода Тоск() Мопъоог.Иаъо(1осКОп); // ожидать завершения метода Тосх() ) Прежде всего обратите внимание иа код в блоке 1осК в методе ТТсК () . Напомним, что методы Иахг () и Рп1зе () могут использоваться только в синхронизированных блоках кода.