Уеб формуляри 4

Задаването на атрибута Async на директивата Page на true казва на ASP.NET, че искаме да използваме асинхронна обработка на заявки в уеб формуляра. Тази функция трябва да бъде активирана изрично, т.к настройването и поддържането на ресурсите, необходими за асинхронни задачи, идва с определени разходи, които не са желателни, когато не е необходима асинхронна обработка.

Атрибутът AsyncTimeout указва броя секунди, които ASP.NET ще изчака изпълнението на асинхронни задачи, преди да изтече времето за изчакване на заявката. В примера е зададено на 60 секунди; ако атрибутът AsyncTimeout не е указан, той по подразбиране е 45 секунди. Една от причините за използване на асинхронна обработка на заявки е да се приспособят дълготрайни операции, така че трябва да коригирате стойността на AsyncTimeout, за да позволите на текущата работа да завърши безопасно.

Асинхронната обработка е реализирана в кодовия файл Default.aspx.cs, чието съдържание е показано в примера по-долу:

Промените изглеждат малки, но вършат много работа. Скоро ще обясним всички подробности, но засега нека да видим резултата. Изображението по-долу показва какво се случи след стартиране на приложението и заявяване на уеб формуляра Default.aspx:

нишки

Общото време, изразходвано за обработка на заявка, получаване на данни от отдалечен уеб сървър и генериране на отговор, се е увеличило леко в сравнение със синхронната опция, обсъдена в предишната статия (това се дължи главно на различни възможности и маршрутизиране в Интернет). Голямата разлика е, че нишката на заявката вече не се блокира, докато чака HTTP заявка към http://professorweb.ru за получаване на отговор.

Чрез прилагане на асинхронна обработка към този уеб формуляр беше възможно нишката на заявката да се върне в пула за период от 1,5 секунди, който беше изразходван за получаване на отговор от сървъра. През това време тази нишка беше достъпна за обработка на други заявки, което доведе до подобряване на общата производителност на приложението.

Получихме резултат, който показва, че няма време за блокиране, но всъщност това е малко по-различно; известно блокиране винаги е налице, дори и да съществува за много кратко време, тъй като е необходимо време за настройка на инфраструктурата, за да се погрижи за необходимата фонова работа. Примерът беше стартиран на локален сървър и измерен до милисекунда - с по-точен таймер и с реалистично натоварване режийните разходи биха били малко по-високи.

Използване на асинхронен метод

Първата промяна, направена във файла Default.aspx.cs, беше да се използва различен метод на клас WebClient:

Вместо синхронния метод DownloadString() се извиква асинхронната версия на DownloadStringTaskAsync(), която връща обект Task и може да се използва с ключовата дума await. Методът DownloadStringTaskAsync() дава същия резултат като метода DownLoadString(), но работи по асинхронен начин, без да блокира нишката на заявката.

Създаване на ваши собствени асинхронни методи

Можете да пренапишете всеки метод, за да върнете обект на Task и да използвате ключовата дума async, но тъй като нишката все още трябва да изпълни метода, рискувате да преместите същия проблем другаде. Най-общо казано, има две изключения от това:

Първото изключение включва предаване на заявката на друг,по-ограничен пул от нишки, като например пула, използван в Task Parallel Library (TPL). Пулът от нишки на TPL използва малък брой нишки и има средства за намаляване на времето, което нишките прекарват в блокиране.

Опасността тук е, че можете да освободите нишка, за да избегнете изчерпването на пула от нишки за връзка, но в крайна сметка да използвате по-малък пул. Например библиотеката TPL разпределя една нишка на процесор и дори с разширени функции е лесно да получите толкова дълга работна опашка, че HTTP заявките да започнат да изтичат.

Второто изключение се дължи на използването на оптимизации на ниско ниво, които повишават ефективността без обвързване на нишки. Добър пример са портовете за завършване на I/O, които операционната система Windows внедрява, за да позволи управлението на голям брой I/O нишки с малък брой нишки. Тази функция се използва от класа WebClient и също така се използва от някои доставчици на връзка с бази данни.

Не забравяйте, че като основно правило, освен ако нямате значителен опит в паралелното програмиране, трябва да прилагате асинхронна обработка на заявки само към ASP.NET Web Forms, като използвате класове на .NET Framework, които предоставят асинхронни методи, като WebClient.

Създайте и регистрирайте задача за асинхронна страница

Трябва да пакетираме асинхронната работа, която трябва да бъде извършена, така че да може да бъде интегрирана в жизнения цикъл, който е дефиниран от класа Page. Това се прави с помощта на класа PageAsyncTask, чийто конструктор взема делегат, който връща обект Task. Използваме ламбда израз като този:

ТоваРазширена употреба на синтаксис на ламбда израз, който опростява дефиницията на фоновата работа, без да изисква създаването на отделен асинхронен метод. Компилаторът на C# прави известна магия с ключовите думи async (което се прилага към ламбда израз) и await (което се използва като префикс при извикване на метод на клас WebClient). Резултатът е делегат, който връща обект Task и извиква метода DownloadStringTaskAsync(), без да блокира нишката на заявката.

Асинхронно действие се регистрира чрез предаване на обект PageAsyncTask към метода RegisterAsyncTask(), дефиниран в класа Page:

Методът RegisterAsyncTask() регистрира асинхронен обект, но неговото изпълнение ще бъде задействано само след събитието PreRender и преди събитието PreRenderComplete.

Изпълнение на множество задачи

Можете да регистрирате множество асинхронни задачи и всички те ще бъдат изпълнени след задействане на събитието PreRenderComplete - обаче последователно, а не паралелно. Това не блокира нишката на заявката, но няма ефекта, който повечето хора очакват, когато се занимават с много асинхронни операции.

За да демонстрираме какво ще се случи, добавихме нов уеб формуляр, наречен Multiples.aspx, чието съдържание е показано в примера по-долу:

Това е вариант на уеб формуляра Default.aspx, който създадохме по-рано. Контролът на повторителя показва набор от резултати. В допълнение, друг тип е дефиниран със свойства, подходящи за този пример - по-специално, ние се интересуваме от началния час на заявката към уеб сървъра, а не от продължителността на времето, през което нишката на заявката е била блокирана, или времето за изпълнение на отделни заявки. Дефинициите на типа и класа на задния код са показани в примера по-долу:

нишки

Извикване на методDataBind() гарантира, че резултатите се показват в контролата Repeater. Причината е свързана с начина, по който контролите, които показват данни, са интегрирани в жизнения цикъл на заявката за страница.

От информацията в колоната "Начален час" можете да видите, че заявките са изпълнени последователно, т.е. заявката към google.com не стартира до около 2,3 секунди, след като е получена от средата на ASP.NET.

Това може да е приемливо за вашето приложение, но повечето приложения изискват да поставите работа на опашка, така че отделните заявки да могат да се изпълняват паралелно, ако има достатъчно налични нишки. Най-лесният начин да постигнете този резултат е с библиотеката TPL - примерът по-долу показва как се прави това във файла Multiples.aspx.cs code-behind:

Ние регистрираме само един обект PageAsyncTask и сами управляваме паралелните задачи с помощта на класа Task. Както бе споменато в предишната статия, TPL библиотеката е отделна голяма тема и няма да обясняваме този код подробно тук. Отбелязваме само, че в резултат на това отделните заявки към съдържанието на уебсайта се поставят на опашка, което ще им позволи да бъдат изпълнявани паралелно:

като

Както можете да видите, и трите заявки бяха изстреляни почти по едно и също време. Това не е гарантирано да се случи, т.к Може да има други задания в опашката и всички TPL нишки може да са заети, но ако има свободни системни ресурси, времето, изразходвано за обработка на заявките, ще бъде много по-малко.