Эмуляция async/await в Delphi

от автора

В C# появилась конструкция async/await. Далее показан вариант как добиться подобного поведения на Delphi.

Я предпологаю, что вы знакомы с async/await. Её удобно использовать для операций, где участие процессора не требуется. Процессор только начинает операцию, а из внешнего мира приходит сообщают об её окончании. Хорошим примером может служить вызов на веб сервер. Допустим наш веб сервер умеет выполнять две операции: сложение и умножение.

        async Task<int> MakeAsyncTask_Plus(int aArg1, int aArg2, int aDurationMs)         {   // эмуляция вызова на веб сервер             await Task.Delay(aDurationMs);             return aArg1 + aArg2;         }         async Task<int> MakeAsyncTask_Mult(int aArg1, int aArg2, int aDurationMs)         {   // эмуляция вызова на веб сервер             await Task.Delay(aDurationMs);             return aArg1 * aArg2;         } 

Клиент хочет вычислить выражение (1+2)*(3+4). Т.к. результаты сложений независимы, их можно выполнять одновременно:

        async void CalcAsync()         {             Task<int> aPlus1 = MakeAsyncTask_Plus(1, 2, 2000);      // 1             Task<int> aPlus2 = MakeAsyncTask_Plus(3, 4, 2000);      // 2               int aArg1 = await aPlus1;                                // 3             int aArg2 = await aPlus2;                                // 4               Task<int> aMult = MakeAsyncTask_Mult(aArg1, aArg2, 1000); // 5             int aRes = await aMult;                                  // 6             Log(string.Format("{0} * {1} = {2}", aArg1, aArg2, aRes));         } 

До строки "//3"(первого await) метод отрабатывает обычным образом. На await происходит выход из метода, а продолжится (до следующего await) по окончанию операции. Управление передастся механизмом сообщений или в другом потоке — настраивается в окружении. В C# это достигается возможностями компилятора. В Delphi похожего поведения на одном потоке можно достичь при помощи Fiber. Выглядеть подобный метод будет похоже:

procedure TCalcAsync.Execute; var   aPlus1: TAsyncTask<Integer>;   aPlus2: TAsyncTask<Integer>;   aMult: TAsyncTask<Integer>;   aArg1, aArg2: Integer;   aRes: Integer; begin   aPlus1 := nil;   aPlus2 := nil;   aMult := nil;   try     aPlus1 := TAsyncTask_Plus.Create(Self, 1,{+}2, 2000{ms});     aPlus2 := TAsyncTask_Plus.Create(Self, 3,{+}4, 2000{ms});       aArg1 := aPlus1.Await;     aArg2 := aPlus2.Await;       aMult := TAsyncTask_Mult.Create(Self, aArg1,{*}aArg2, 1000{ms});     aRes := aMult.Await;     Example.Log('%d * %d = %d', [aArg1, aArg2, aRes]);   finally     aMult.Free;     aPlus2.Free;     aPlus1.Free;   end; end; 

Пример работает для Delphi 2007, XE2, XE8.

ссылка на оригинал статьи https://habrahabr.ru/post/278985/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *