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