Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 108
Текст из файла (страница 108)
Теперь давайте внимательно рассмотрим интерфейс 1Азупскезц11 объекта, возвращеииого методом Вес1птпчохе. Объявление интерфейса выглядит следующим образом: рцЬ11с апгеггасе 1Азупслеэц11 ( Оь)есс Азупсзьаье ( сепг ) Иа1ЬНапб1е Азупсиа11Напб1е ( Зесг ) Ьоо1 Сошр1егес(вупспгопоцз1у ( оег) ) Ьоо1 1зсошр1есес( ( Сегг ) ) В предыдущем примере было решено ждать окончания вычислений, вызвав Епс)1пчохе.
Вместо этого можно было бы ожидать Иа1ГНапс)1е, возвращенного свойством 1азупспезц1Г .АзупсХа1СНапс)1е перед вызовом Епс)1пчохе. В любом случае конечный результат был бы таким же. Однако тот факт, что интерфейс 1Аэупснезц11 пред- Многопоточность в С() 405 ставляет ЫаТСНапб1е,позволяет при необходимости иметь несколько потоков в системе, ожидающих завершения одного действия. Два других свойства позволяют проверить, завершена ли операция. Свойство 1зСогяр1егес( просто возвращает булевское значение, представляющее этот факт. Можно быяо бы организовать цикл по пулу, периодически проверяющий значение этого флага. Однако это оказалось бы намного менее эффективно, чем просто ожидание МаТСНапб1е. Но, тем не менее, существует и такой вариант. Второе булевское свойство — это Согяр1егес(Зупс)тгопооз1у.
Шаблон асинхронной обработки в .)ч)ЕТ Ргагпетчог)с пре)пгсматривает возможность вызова Вецьп1п1го)се для запуска работы в синхронном, а не асинхронном режиме. Свойство Согпр1егес(Яусс)тгопопз1у позволяет определить, если это произойдет. В текущей реализации СЬВ никогда этого не делает, когда делегаты вызываются асинхронно, но это может измениться в любое время. Однако, поскольку рекомендуется применять тот же асинхронный шаблон всякий раз, когда проектируется тип, который может быть вызван асинхронно, такая возможность была встроена в шаблон. Например, предположим, что у вас имеется класс, в котором поддерживается метод для синхронного выполнения обобщенных операций. Если одна из таких операций просто возвращает номер версии класса, это значит, что она завершается быстро, и ее можно выполнять синхронно.
И,наконец, свойство )(зупсесасе интерфейса Тйзупскезо1с позволяет присоединить к асинхронному вызову любой тип специфичных контекстных данных. Это — второй из двух дополнительных параметров, добавленных в конец сигнатуры Ведуптпчохе. В предыдущем примере был передан по11, потому что этот параметр был не нужен. Хотя результат операции получается через вызов Епг(1пчо)ге, таким образом, блокируя поток, можно было бы предпочесть получать уведомление о завершении операции через обратный вызов.
Рассмотрим следующую модификацию показанного ранее примера: ивгпо Бувгеиг овьп9 Яузгеи.т)тгеаг(1пч; ров11с с1авв ЕпггуРоьпг ( г'г' Объявление делегата для асинхронного вызова. ргучаге бе1ечаге Оес1иа1 СоирогеТахевбе1едаге( хпС уеаг ) О Метод для вычисления налогов. ргьнаге вгаСТс Оес1за1 СоирогеТахев( Тпг уеаг ) ( Сопво1е.игэгевэпе( "Вмчисление налогов в потоке (О)", Т)тгеаб.соггепгтнгеаб.Мападебтпгеаг(1б ) О Здесь происходит длительное вычисление. Тнгеаг(.81еер( 6000 ); гегигп 4356.98М; ) ргучаге вгагэс чо10 Тахевсоирогеб( 1)(зупсвезо1С аг ) ( г'г' Теперь получим результат. СоирпгеТахевбе1едаге нпгс = (СоирогеТахевве1ечаге) аг.двупсЗСагег Оесхиа1 гево1С = ног)г.впс(1пчосе( аг ); Сопзо1е.ыгссеь1пе( "Сумма налогов: (О)", гезн1С ); ) вгагус чогб Маеп() ( /г' Выполним асинхронный вызов, создав делегат и вызвав его. Соирогетахезбе1еоаге ног)с = пен СоирогеТахевпе1еоаге( Епггуроэпс.СоирнгеТахез ); 406 Глава (2 ногК.Вед1п1пчохе( 200Я, пен АзупсСа11Ьасх( Епсгугогпк.ТахеэСошрцсеб), ногх )) г'г' Выполнить другую полезную Работу.
Тпгеаб.Я1еер( 3000 )) ГГ Завершить асинхронный вызов. Сопзо1е.игзкетзпе( "Ожидание завершения операции." )) // Я1еер используется только для примера! // В действительности необходимо ожидать событие, О чтобы получить результат или что-то подобное. Тнгеаб.Я1еер( 4000 ): Теперь вместо обращения к Епб1пчойе из потока, который вызвал Ведзп1пчойе, пул потоков должен вызывать метод ТахезСошрцкес) через экземпляр делегата АэупсСа11ЬасК, который передан в предпоследнем параметре Ведзп1пчойе.
Делегат 1АзупсСа11ЬасК ссылается на метод, принимающий один параметр типа 1Азупснезц10 и возвращающий чозб. Использование обратного вызова для обработки результата довершает шаблон асинхронной обработки, позволяя потоку, запустившему операцию, продолжать работу, без необходимости явного ожидания рабочего потока. Обратите внимание, что метод обратного вызова ТахезСошрцгег( для получения результата асинхронного вызова все равно должен вызвать Епб1пчойе.
Чтобы сделать это, однако, ему нужен экземпляр делегата. И здесь на помощь приходит объект контекста 1Азупснезц1С.Азупсзсасе. В рассматриваемом примере Тйзупснезц1Г.АзупсЯкаге инициализируется так, чтобы указывать на делегат, который передается в последнем параметре Ведап1пчо)се. Главный поток, вызывающий Ведзп1пчойе, не нуждается в объекте, возвращенном этим вызовом, поскольку он никогда в действительности не опрашивает состояния операции, нак и не ожидает явно ее завершения. Задержка с помощью Я1еер добавлена в конец метода Ма).п просто для примера. Помните, что все потоки в пуле выполняются нак фоновые.
Поэтому если не ожидать в этой точке, то процесс завершится задолго до завершения операций. Если вы хотите, чтобы асинхронная работа проходила в потоке переднего плана, лучше создайте новый квасе, реализующий асинхронный шаблон Ведзп1пчойе /Еп61пчойе и используйте поток переднего плана для выполнения работы. Никогда не меняйте фоновый статус потока в пуле через свойство 1звасйдгоцпс( в текущем потоке. Даже если попробовать это сделать, это не даст никакого эффекта. На заметку! Важно понимать, что когда выполняется асинхронный код и осуществляется обратный вызов, работа происходит в контексте произвольного потока. Нельзя делать какие-либо предположения относительно того, какой поток будет выполнять код.
Во многих отношениях эта техника подобна разработке драйверов на платформе УУ(пбоч(з. Применение обратных вызовов для обработки завершения элемента работы очень удобно при создании серверного процесса, обрабатывающего входящие запросы. Например, предположим, что есть процесс, прослушивающий определенный порт ТОР/1Р на предмет входящих запросов.
Когда он получает такой запрос, то отвечает на него, пересылая запрошенную информацию. Чтобы достичь мансимальной эффективности, эти операции определенно должны выполняться асинхронно. Рассмотрим следующий пример, который прослушивает порт 1234 и, получив что-либо, просто отвечает отправкой строки "Не!!о йгог)б)"; Многопоточность в С() 407 нвбпз Бунгало пвбпд Яувсеш.Техст пабло Яувгеш.ТЬгеаббпдт пвупч Яуясеш.вест сяьпч Буясеш.нес.зоснесят рнЬ11с с1аяя ЕпсгуРо1пс ( ргбчаге сопят Ьпс соппесгОнеиеьепсгь = 44 рггчасе сопяТ Ьпг ЬгвсепРогс = 1234г ягас1с чо1б ьбвгепрогВечпеягв() ( Яосхес 11ясепзосн = пен Яоснес( Аббгезвраш11у.
1псегнесногн, Босхестуре.зсгеаш, Ргососо1Туре.Тср ); 11ясепзосХ.Вгпб( пен 1РЕпбРоЬпс(1РАббгезв.дпу, Ьбзсепрогс) )т 11ясепзосх.ЬЬясеп( СоппесГОпеоеьепдГП ); нн11е ( Ггне ) ( пя1пч( Босхес пенСоппессгоп = 11ясепзосн.зегерс() ) ( /l Отправить данные. Ьуге() шяо = Епсобтпд.ОТЕЯ.аеГВусев( "Неууо Ног1б(" ); пенСоппессбоп.Яепб( шве, Босхегр1аов.носе ); ) зсасбс чогб Иатп() г'г' Запустить прослушизаюший поток. Тпгеаб 11всепег = пен Тпгеаб( пен Тпгеабясагс( ЕпсгуРоьпг.ЬЬясепрогВецсезся) ) 11зсепег.1вВасгдгонпб = ггнет 11вгепег.ягагг()) Сопво1е.иг1сеЬ1пе( "Нажмите <Епсег> для завершения" )т Сопяо1е.кеабЬ1пе()г В коде примера создается дополнительный поток, который просто в цикле прослушивает входящие соединения и обслуживает их по мере поступления.
С этим подходом связано много проблем, одна из которых состоит в том, что входящие соединения обрабатывает только один поток. Если соединения начнут поступать очень часто, поток быстро окажется перегруженным. Например, реальный веб-сервер может получать тысячи запросов в секунду. Класс Яосхег реализует шаблон асинхронных вызовов .Р)ЕТ ггашетчог)г. Используя этот шаблон, можно улучшить сервер, обслуживая входящие запросы с применением пула потоков, как показано ниже: пвьпз Яуягешт пябпч Бувсеш.техт) нябпд 5уясеш.ТПгеаб1пдт пабло 5увсеш.нес) пвбпд Яувсеш.нес.зосиесвт рнЬ11с с1азв ЕпсгуРо1пс ( рг1чаге сопят гпг соппесгОпепеьепчгь = 44 ргбчаге сопят Ьпс ЬбвгепРогс = 12344 згасбс чобб ЬбясепуогНецневгв() ( 408 Глава (2 Босхег 11вгепБос)с = пен Босхег( Аббгеэзраш11у.1пгегнегног)с, Босхегтуре.БСгеаш, Ргогосо1Туре.Тср ); 11згепБосЕ.ВЬпб( пем 1РЕпбРо1пг(1РАббгеэз.Апу, ЬазгепРогС) 11эгепБосХ.Ь1эгеп( СоппесСОпепеьепдгп ); нП11е( Сгпе ) ( Яссаее пеиСоппесеаоп 11веепаосК.Ассерв(); ЬуСе[] шэс = Епсобтпя.нтРЯ.СеСВугеэ( "Не11о Иог1б!" ); пеиСоппесеаоп.Вед1паепб( швд, О, шве.ьепдСЬ, ЗссаеСУ1адв.косе, пп11, пп11 )г эгаС1с чо1б Мв1п() // Вапустить прослушиваюший поток.
Тпгеаб 11зСепег = пем Тпгевб( пен тпгеабБСагг( ЕпггуРо1пС.ЬЬвгепуогкейпеэгв) 11вгепег.1эВасКЯгоппб = Сгпе; 11згепег.БСагС(); сопво1е.игтсеьбпе( "нажмите <епсег> лля завершения" ); Сопво1е .ЕевбЬЬпе () 1 Сервер стал немного эффективнее, поскольку теперь отправка данных для входящих соединений происходит в потоке, взятом из пула. Этот код также демонстрирует стратегию "сделал и забыл", характерную для использования асинхронного шаблона. Вызывающий код не заинтересован в возврате объекта.










