Г. Шилдт - С# 3.0 Полное руководство. 2010 (1160798), страница 164
Текст из файла (страница 164)
// Испольэовать событийный объект, устанавливаемый в // в исходное состояние вручную. овгпо Яуякевп ояьпд яуякею.тпгеабгло/ // этот поток уведомляет о том, что событие передано // его конструктору. с1аяя Мутпгеаб ( Глава 23.
Многопоточное программирование 825 рпЬ11с ТЬгеаб ТЬгт(т Маппа1неяеСЕчепг жгет рпЬ11с МУТЬгеаб(яггспц паше, Маппа1пеяеСЕчепг ечг) ( ТЬгб пен ТЬгеаб(СЬ1я.нпп)) тнгб.наше = паве; шге = ечгт тьгб. Всагс () ) ) О Точка входа в поток. чоаб Епп() ( сопяо1е.иг1сеьапе("Внутри потока " + тЬгб.маше)) Тог (1пг 1=От 1<5 т 1++) ( Сопяо1е.Игасе11пе(ТЬгб.наше) ТЬгеас(.51еер(500)) ) Сопяе1е.Иг1Се11пе(ТЬгб.наше + " завершен™); // Уведомить о событии.
шге.аег()Г ) ) с1аяя Маппа1ЕчепСОешо ( ясас1с чогб Маап () [ маппа1яеяесечепс ечсбьр = пен маппа1кеяесечепс(та1ве)т МуТЬгеаб шС1 = пен МУТЬгеас)("Событийный Поток 1", ечСОЬ))т Совяо1е.Игасетапе("Основной поток ожипает событие.")г // Ожидать уведомления о событии. ечсоьр.мавсоле М г Сопяо1е.нгагеВТпе("Основной поток получил " + "уведомление о событии от первого потока.") // Установить событийный обьект в исходное состояние. ечСОЬр.пеяег()т шС1 = пен Мутнгеаб("Событийный Поток 2", ечСОЬ5) // Ожидать уведомления о событии. ечсОЬ).иа1сбпе () ' сопяо1е.иг1се11пе("Основной поток получил " + "уведомление о событии от второго потока.") ) ) 826 Часть )). Библиотека ОЕ Ниже приведен результат выполнения рассматриваемой здесь программы, хотя у вас он может оказаться несколько иным.
В потоке Событийный Поток 1 Событийный Поток 1 Основной поток окидает событие. Событийный Поток 1 Событийный Поток 1 Событийный Поток 1 Событийный Поток 1 Событийный Поток 1 завершен! Основной поток получил уведомление о событии от первого потока. В потоке Событийный Поток 2 Событийный Поток 2 Событийный Поток 2 Событийный Поток 2 Событийный Поток 2 Событийный Поток 2 Событийный Поток 2 завереен! Основной поток получил уведомление о событии от второго потока.
Прежде всего обратите внимание на то, что событие типа маппа1йеяескчепг передается непосредственно конструктору класса МуТЬгеаб. Когда завершается метод поп() из класса Мутпгеас(, он вызывает для событийного объекта метод Вег (), Устанавливающий этот объект в сигнальное состояние. В методе ма1п () формируется собьпийный объект ечСОЬб типа Маппа1кеяеСЕчепг, первоначально устанавливаемый в исходное, несигнальное состояние. Затем создается экземпляр объекта типа муТЬгеак), которому передается событийный объект ечСОЬл. После этого основной поток ожидает уведомления о событии.
А поскольку событийный объект ечСОЬб первоначально находится в иесигнальном состоянии, то основной поток вынужден ожидать до тех пор, пока для экземпляра объекта типа мутьгеак( не будет вызван метод Вес (), устанавливающий событийный объект ечсоЬл в сигнальное состояние. Это дает возможность основному потоку возобновить свое выполнение. Затем событийный объект устанавливается в исходное состояние, и весь процесс повторяется, но на этот раз для второго потока. Если бы не событийный объект, то все потоки выполнялись бы одновременно, а результаты их выполнения оказались бы окончательно запутанными.
Для того чтобы убедиться в этом, попробуйте закомментировать вызов метода маътопе () в методе ма1п () . Если бы в рассматриваемой здесь программе событийный объект типа йпсопеяесдчепс использовался вместо событийного объекта типа маппа1пеяескчепс, то вызывать метод пеяес () в методе мабп () не пришлось бы. Ведь в этом случае событийный объект автоматически устанавливается в несигнальное состояние, когда поток, ожидающий данное событие, возобновляет свое выполнение. Для опробования этой разновидности события замените в данной программе все ссылки на объект типа маппа1пеяескчепс ссылками на объект типа йпгодеяеткчепт и удалите все вызовы метода Пеяег Н. Видоизмененная версия программы будет работать так же, как и прежде. Класс Хп~ех1ос)сесе Еще одним классом, связанным с синхронизацией, является класс Тот ег1ос Хек).
Этот класс служит в качестве альтернативы другим средствам синхронизации, когда требуется только изменить значение обШей переменной. Методы, доступные в классе Тптег1ос)кеб, Глава 23. Многопоточное программирование 827 гарантируют, что их действие будет выполняться как единая, непрерываемая операция.
Это означает, что никакой синхронизации в данном случае вообще не требуется. В классе 1псег1ос)тес( предоставляются статические методы для сложения двух целых значений, инкрементирования и декрементирования целого значения, сравнения и установки значений объекта, обмена объектами и получения 64-разрядного значения.
Все эти операции выполняются без прерывания. В приведенном ниже примере программы демонстрируется применение двух методов из класса 1псег1осхет(: 1псгевепс () и Ресгевепг () . При этом используются следую(цие формы обоих методов: роЫЕс всастс гпс 1псгевепс(геЕ Епс у) рпЫЕс всас1с Епс Оесгевепс(гет Епс у) где у обозначает инкрементируемое или декрементируемое значение. // Использовать блокируемме операции. цв1пд Бувсепм пвтпд Бувсев.ТЬгеабъпд) // Общий ресурс. с1авв Бпагет(аев ( рцытс вгаг1с Епг соппг = От ) // В этом потоке переменная Бьагепвев.соппг инкрементируется. с1авв 1пстцгеаб [ роЫЕс Тнгеаб Тнгб) рцЫЕс 1пстпгеат)(всгтпд паве) ( Тпгб = пен Тпгеаб(ГЫв.впп)т тьгб.паве = паве; Тнгб.зсагс()) ) // Точка виола в поток.
то1б Воп() ( Еог(тпс 1=01 1<51 1++) ( 1псег1оскет(.1псгевепс(геЕ Бнагеоаев.соцпс)т Сопво1е.иг1сеъ1пе(тнгт(.паве + " Соцпс = " ч Бнагеснев.Соцпс)) ) ) // В этом потоке переменная Бпагет(вев.соцпс ЛЕкРементиРУется. с1авв Оестпгеаб ( рцЫЕс Тпгеаб Тпгт() рцЫЕс Оестпгеаб(всггпд паве) ( Тнго = лен Тпгеаб(ГП1в.кцп)т тнгб.паве = павел Тнгб.зсагс()т 828 Часть И. Библиотека С() // Точка входа в поток. ъ.о1с( нпп () ( Еог(гпг 1=0; 1<5» 1++) ( 1псег1осхес(.оесгешепс(гет зпагес(вея.сонно)» Сопяо1е.иг1сеЬ1пе(тпго.наше + " Сочно = " + зпагепнея.сочло)) ) ) ) с1аяя 1псег1осгеопеюо ( ясасгс чогд Мага() ( // Сконструировать два потока.
гпстпгеап п|Г1 = пен 1псТЬгеап("Инкрементируюший Поток" ); Оестпгеао шг2 = пен Оестпгеап("Декрементируюший Поток" ); шс1. Тпго. )огп () ) шг2 . Тпгя(. Тогп () ) ) ) Прерывание потока Иногда полезно прервать поток до его нормального завершения. Например, отладчику может понадобиться прервать вышедший из под контроля поток. После прерывания поток удаляется из системы и не может быть начат снова. Для прерывания потока до его нормального завершения служит метод ТЬгеас).
АЬогт. Н . Ниже приведена простейшая форма этого метода. рчЬ11с чоюд АЬогс () Метод Аьогс () создает необходимые условия для генерирования исключения ТЬгеас)АЬогснхсерсгоп в том потоке, для которого он был вызван. Это исключение приводит к прерыванию потока и может быть перехвачено в коде программы, но в этом случае оно автоматически генерируется еще раз, чтобы остаНовить поток. Метод АЬогг () не всегда способен остановить поток немедленно, поэтому если поток требуется остановить перед тем, как продолжить выполнение программы, то после метода АЬогс () следует сразу же вызвать метод,тогп ( ) , кроме того, в самых редких случаях методу Аьогс ( ) вообще не удается остановить поток. Это происходит, например, в том случае, если кодовый блок Ггпа11у входит в бесконечный цикл.
В приведенном ниже примере программы демонстрируется применение метода АЬогг () для прерывания потока // Прервать поток с помошью метода АЬогс(). иягпд зуясею) чягпд эуясею.ТЬгеапгпо; с1аяя Мутпгеао ( рпЬ11с Тпгеас) Тпгс(; Глава 23. Многопоточное нрограммнроеанне 829 рпЬ11с Мутнгеаб(яШп9 паве) ( ТЬгб = пеы ТЬгеаб(ГЬ1я.Кпп)7 ТЬгб.паве = паве; ТЬгб . Ясагс () Г ) // Это точка входа в поток. чотб Кпп() ( сопяо1е.игтгеьтпе(тьгб.маше + " начат."); Тог(ьпс 1 = 1г 1 <= 10007 1++) ( Сопяо1е.игтсе(1 + " ")7 1 Т ( (1%10) ==О) Сопяо1е. Игтсеьбпе () ) ТЬгеаб.81ееР (250)( ) Сопво1е.ИгтгеЬТпе(ТЬгб.паве + " вавершен.")) ) ) с1аяя Ягорпево ( ясастс чотб Мвтп О ( мутьгеаб вг1 = пен мутьгеаб("мой поток"); ТЬгеаб.81еер(1000)) // разрешить порожденному потоку // начать свое выполнение Сопяо1е .Игтсе) Тле (" Прерывание потока . ") вс1.ТЬгб.кьоге () 7 вс1.ТЬгб.боьп()г // ожидать прерывания потока Сопяо1е.игьсеЬТпе("Основной поток прерван."); ) Вот к какому результату приводит выполнение этой программы: Мой Поток начат 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 Прерывание потока.
Основной поток прерван. На заметку! Метод ЬЬогг () нв следует применять в качестве обычного средства прерывания потока, поскольку он предназначен для особых случаев. Обычно поток должен завершаться вствсгпввнным образом, чтобы произошел возврат из метода, выполнягощего роль точки входа в него. 830 Часть П. Библиотека СЯ Другая форма метода дЬох~ () В некоторых случаях оказывается полезной другая форма метода АЬогг (), общая форма которого приведена ниже. рпЬ11с чо1т( АЬогс (оЬбесс кпго) где дпго обозначает любую информацию, которую требуется передать потоку, когда он останавливается. Эта информация доступна посредством свойства ехсерг1опЯСасе из класса исключения ТЬгеас)АЬогткхсерт1оп. Подобным образом потоку можно передать код завершения.
В приведенном ниже примере программы демонстрируется применение данной формы метода АЬогГ () . // Использовать форму метода АЬогг(оЬбесс Тито). пятну Яуягепп пв1пд Яуясеш.ТЬгеаотпд) с1аяя Мутьгеад ( рпЬ11с тьгеас Тпгт)) рпЬ11с Мутпгеас(ягг1пч папе) ( тьго' = пеи тьгеас(гптя.кпп)т ТЬге.наше = паше) тьге.ягагг()т ) // Это точка входа в поток. чо1П Нпп() ( сгу ( Сопяо1е.нг1сеЬ1пе(тпгс.паше т " начат."); Гог(1пг 1 = 1т 1 <= 1000т 1н) ( Сопяо1е.Игдсе(1 + " ")т 11((гя10)==0) ( Сопяо1е.нггаентпе()т тнгеао.я1еер(250)т ) ) Сопяоте.нгтсе11пе(тпгт).наше + " завершен нормально.")т ) сагсп(тпгеаПАЬоггехсерстоп ехс) ( Сопяо1е.нг1пеЬ1пе( "Поток прерван, код завершения " + ехс.Ехсерс1опзгасе)т с1авя ПяеА1ГАЬогг ( ясагтс чотс магп() ( Мутпгеап шс1 = пен МуТЬгеас("Мой Поток" ); Тпгеас.Я1еер(1000)т // разрешить порожденному потоку // начать свое выполнение Сопяо1е.нгтсе11пе("Прерывание потока."); Гааза 23.