Г. Шилдт - С#4.0 Полное руководство (1160795), страница 175
Текст из файла (страница 175)
В потоке Событийный Поток 1 Событийный Поток 1 Основной поток ожидает событие. Событийный Поток 1 Событийный Поток 1 Событийный Поток 1 Событийный Поток 1 Событийный Поток 1 завершен! Основной поток получил уведомление о событии от первого потока. В потоке Событийный Поток 2 Событийный Поток 2 Событийный Поток 2 Событийный Поток 2 Событийный Поток 2 Событийный Поток 2 Событийный Поток 2 завершен! Основной поток получил уведомление о событии от второго потока.
Прежде всего обратите внимание на то, что событие типа Маппа1неяеСЕчепс передается непосредственно конструктору класса МуТЬгеаб. Когда завершается метод Ппп () из класса МуТЬгеаб, он вызывает для событийного объекта метод Бес (), устанавливающий этот объект в сигнальное состояние. В методе ма1п () формируется событийный объект ечСОЬ) типа Маппа1неяеСЕчепб, первоначально устанавливаемый в исходное, несигнальное состояние. Затем создаетгя экземпляр объекта типа Глава 23.
Многопоточное программирование. Часть первая: основы 873 МуТЬгеаг(, которому передается событийный объект ечгОЬз. После этого основной поток ожидает уведомления о событии. А поскольку событийный объект ечСОЬз первоначально находится в несигнальном состоянии, то основной поток вынужден ожидать до тех пор, пока для экземпляра объекта типа МуТЬгеас) не будет вызван метод ЯЕС () гУСтаиаВЛИВаЮЩИй СОбЫтИйНЫй ОбЪЕКт ЕЧСОЬЗ В СИГНаЛЬНОЕ СОСТОЯНИЕ. ЭтО дает возможность основному потоку возобновить свое выполнение. Затем событийный объект устанавливается в исходное состояние, и весь процесс повторяется, но на этот раз для второго потока.
Если бы не событийный объект, то все потоки выполнялись бы одновременно, а результаты их выполнения оказались бы окончательно запутанными. Для того чтобы убедиться в этом, попробуйте закомментировать вызов метода Навгопе () в методе Мапл () . Если бы в рассматриваемой здесь программе событийный объект типа Ац Коневе сечен с использовался вместо событийного объекта типа мапо а1ре яе сече ос, то вызывать метод йеяег ( ) в методе Мавп ( ) не пришлось бы. Ведь в этом случае событийный объект автоматически устанавливается в несигнальное состояние, когда поток, ожидающий данное событие, возобновляет свое выполнение. Для опробования этой разновидности события замените в данной программе все ссылки на объект типа мапца1неяесечепс ссылками на объект типа Ацсонеяесечепс и удалите все вызовы метода невес () .
Видоизмененная версия программы будет работать так же, как и прежде. Кйасс Хп~ег1ос)сей Еще одним классом, связанным с синхронизацией, является класс 1цгег1оскес). Этот класс служит в качестве альтернативы другим средствам синхронизации, когда требуется только изменить значение общей переменной. Методы, доступные в классе 1пгег1оскег), гарантируют, что их действие будет выполняться как единая, непрерываемая операция.
Это означает, что никакой синхронизации в данном случае вообще не требуется. В классе 1нгег1осхег( предоставляются статические методы для сложения двух целых значений, инкрементирования и декрементирования целого значения, сравнения и установки значений объекта, обмена объектами и получения 64-разрядного значения.
Все эти операции выполняются без прерывания. В приведенном ниже примере программы демонстрируется применение двух методов из класса 1пгег1осхесп 1псгевепг () и Оесгевепг () . При этом используются следующие формы обоих методов: рцЫьс ясяс1с ьля глстевепс(геГ 1цс 1осасзол) рцыьс ягагьс ьцг оесгевепс (гег ьпг 1ясаыел) где 1оса С1оп — это переменная, которая подлежит инкрементированию или декре- ментированию. !/ Использовать блекируеиые операции. цяьцп ЯуясевГ ця1пд Яуягев.тлгеабьцяп !l Обвив ресурс. с1яяя ЯлягесЯея ( рцЫьс ягаяьс 1пг Сваля = 0; ) 874 Часть 11.
Библиотека С№ // В этом потоке переменная ЯЛагебдея.сонях инкрементируется. с1аяя тпсТЛгеаб ( роЬ11с Тьгеаб ТЛгб) роЬ11с 1пстдхеаб(ягх1по папе) ( ТЛгб = пен ТЛгеаб(ГЛ1я.Поп); ТЛхб.нате = папе; ТЛгб.есагх(); О Точка входа в поток, но1б Впп() ( Гог(гпх 1=0; 1<5; 1++) ( тпхег1ос)себ. 1псхевепг (гет ЯЛагебнея .Сонях) П сопяо1е.игггеьгпе(тлгб.мате я " соопг = " я ялагебдея.соппг)п ) // В этом потоке переменная ЯЛагебдея.Сопят декрементируется. с1аяя ОесТЛгеаб ( роЫ1с ТЛгеаб ТЛгб; рпЫ1с ОесТЛгеаб(ясггпд паве) ( ТЛгб = пен ТЛгеаб(ГЛ1я.ноп)п ТЛгб.нате = пате; тдгб.згагг(); ) Точка входа в поток.
но1б Коп() ( Гог(гпх 1=0; 1<5; 1++) ( тпхег1оскеб.оесгетепх(гет ЯЛагебдея.сонях)) Сопво1е.игххеь1пе(ТЛгб.наве + " Сових = " + яьагебнея.Соопх)„. с1аяя тпсег1осьебоево ( ягагтс нояб Мати() ( Сконструировать два потока. 1пстьгеаб вг1 = пен тпстдгеаб("инкрементируювий Поток" ) ОесТЛгеаб вГ2 = пен Оестьгеаб("Пекрементируиций Поток" ) тх1 .ТЛгб. Того (); тх2 .ТЛгс(.
тоти (); ) ) Классы синхронизации, внедренные в версии .НЕТ Ггагве)(((ого 4.0 Рассматривавшиеся ранее классы синхронизации, в том числе ЯетарЛохе и ))псонеяегВнепл, были доступны в среде .(1(ЕТ Ргагпетног(с, начиная с версии 1.1. Глава 23. (у)иогопоточиае программирование. Часть первая: основы 875 Таким образом, эти классы образуют основу поддержки синхронизации в среде .)х(ЕТ Егагпеи от)г. Но после выпуска версии .)ЧЕТ Ргатеьуог)г 4.0 появился ряд новых альтер- натив этим классам синхронизации.
Все они перечисляются ниже. Назначение Еагггег Вынуждает потоки ожидать появления всех остальных пото- ков в указанной точке, называемой барьерной Выдает сигнал, когда обратный отсчет завершается Это упрощенный вариант класса маппа1ееяегеуепг Зго упрощенный вариант класса Яеварпоге Соипсс(оипЕуепс Маппа1ЕеяеГЕуепГЯ11в ЯеварЬогеЯ11в Если вам понятно, как пользоваться основными классами синхронизации, описанными ранее в этой главе, то у вас не должно возникнуть затруднений при использовании их новых альтернатив и дополнений.
Прерывание потока Иногда поток полезно прервать до его нормального завершения. Например, отладчику может понадобиться прервать вышедший из-под контроля поток. После прерывания поток удаляется из системы и не может быть нача~ снова. Для прерывания потока до его нормального завершения служит метод ТЬгеаг) . АЬогс () . Ниже приведена простейшая форма этого метода. рагс га16 АЬогс () Прервать поток с помощью метода АЬогс(). азгпу Яуягев; ияьпд Яуясев.тпгеаагпЧГ с1аяя Мутпгеап' ( риЬ11с Тпгеае Тпггб риЫгс МуТЬгеаа(ясггпд паве) Таге' = пек Тьгеае(ГЫя.пеп)г Тпгп.паве = паве; Тагг).зсагс(); ) Метод АЬогс () создает необходимые условия Для генерирования исключения ТЬгеаг(АЬоггЕхсерсзоп в том потоке, для которого он был вызван. Это исключение приводит к прерыванию потока и может быть перехвачено и в коде. программы, но в этом случае оно автоматически генерируется еще раз, чтобы остановить поток.
Метод АЬогс () не всегда способен остановить поток немедленно, поэтому если поток требуется остановить перед тем, как продолжить выполнение программы, то после метода АЬогс ( ) следует сразу же вызвать метод Поьп () . Кроме того, в самых редких случаях методу АЬогс () вообще не удается остановить поток. Это происходит, например, в том случае, если кодовый блок бпа11у входит в бесконечный цикл. В приведенном ниже примере программы демонстрируется применение метода АЬогс () для прерывания потока.
876 Часть!). Библиотека С() // Это точка входа в поток. чогс) Хоп() ( Сопяо1е.нгьгеЬ1пе(тьгг).Наше + " начат."); Тот(1пс ь = 1; 1 <= 1000; г++) ( Сопяо1е.игьпе(1 ь " "); ьг((ь910)==0) ( Сопяо1е.ит1яестпе()г Тьтеаг(. 81еер (250); ) сопяо1е.хггсеьгпе (тьгг(.наше + " завершен.") г ) ) с1аяя зпороешо ( яяаггс чоха Маги() ( Мутьтеаз шо1 = пен Мутьлеаг(("Мой Поток" ); ТЬхеаг(.81еер(1000); // разрешить порожденному потоку начать свое выполнение Сопяо1е.Хх1певьпе("Прерывание патока."); шв1.ТЬгс1.ЛЬогп(); ш11.ТЛге.согп(); // ожидать прерывания потока Сопяо1е.нггпе11пе("Основной поток прерван.") ) Вот к какому результату приводит выполнение этой программы. Мой Поток начат 1 2 3 4 5 б 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 Зб 37 38 39 40 Прерывание потока.
Основной поток прерван. ПРИМЕЧАНИЕ Метод АЬогг () не следует применять в качеСтвЕ Обычного средства прерывания потока, поскольку он предназначен для особых случаев. Обычно поток должен завершаться естественным образом, чтобы произошел возврат из метода, выполняющего роль точки входа в него. Другая форма метода а13ох~ () В некоторых случаях оказывается полезной другая форма метода АЬогс (), приведенная ниже в общем виде: Глава 23. Многопоточное программирование. Часть первая: основы 877 риЫ1с чо1б АЬотт(оЬзест ятаге1пго) где яса сетпГо обозначает любую информацию, которую требуется передать потоку, когда он останавливается. Эта информация доступна посредством свойства ехсерС1опБсасе из класса исключения тнтеабАЪотсехсерс1оп.
Подобным образом потоку можно передать код завершения. В приведенном ниже примере программы демонстрируется применение данной формы метода АЬотг () . О использовать форму метода АЬотг (оЬ)есг ясаге1пто) . пятно Буятепо пя1по 5узтев.ТЬтеаббпдт с1аяя НуТЬтеаб ( рпЫтс Тпгеаб Тптб; рпЫгс Мутпгеаб(ятггпд паве) Тптб = пеи Тпгеаб(сЫя.нчп); Тпгб.паве = паве; тпгб.зтагт()т Это точка входа з поток. чотб Кип() ( ггу ( сопяо1е.ит1гетбпе(тьгб.натпе + " начат."); тот(гпт г = 1т 1 <= 1000т 1+т) ( Сопяо1е.игбсе(т + " ")т 11( (1%10) ==О) ( Сопяо1е.нггсешпе()т тпгеаб.51еер(250)т ) Сопяо1е.нт1тепгпе(ТЬтб.паве + " завершен нормально."); сатсл(тпгеабВЬогтЕхсерттоп ехс) ( Сопяо1е.нгтгепьпе("Поток прерван, код завершения ехс.ехсертгоп5тате)т ) с1аяя Пяел1ЬЛЬогт ( ятат1с нота Нагл() ( Нутптеаб вт1 = лен НУТЬгеаб("Ной Поток" ); Тпгеаб.51еер(1000)т // разрешить порожденному потоку начать свое выполнение Сопяо1е.нгттешпе("Прерывание потока."); вт1.тптб.АЬотт(100)т вт1.ТЬтб.аотп()т // ожидать прерывания потока 878 Часть ().