ݺߣ

ݺߣShare a Scribd company logo
Поговорим про async/await в C#
Синхронный метод
static void Main(string[] args)
{
string googleResult = new HttpClient().GetString("https://google.com/"); // 1
Console.WriteLine($"Google loaded! Content length: {googleResult.Length}");
string yandexResult = new HttpClient().GetString("https://yandex.ru"); // 2
Console.WriteLine($"Yandex loaded! Content length: {yandexResult.Length}");
}
Асинхронный метод (.NET 4.0)
static Task MainAsync()
{
return new HttpClient().GetStringAsync("https://google.com")
.ContinueWith(task =>
{
Console.WriteLine($"Google loaded! Content length: {task.Result.Length}");
return new HttpClient().GetStringAsync("https://yandex.ru");
}).Unwrap() // Task<Task<string>> => Task<string>
.ContinueWith(task =>
{
Console.WriteLine($"Yandex loaded! Content length: {task.Result.Length}");
});
}
Асинхронный метод (.NET 4.5)
public static async Task MainAsync()
{
string googleResult = await new HttpClient().GetStringAsync("https://google.com/");
Console.WriteLine($"Google loaded! Content length: {googleResult.Length}");
string yandexResult = await new HttpClient().GetStringAsync("https://yandex.ru");
Console.WriteLine($"Yandex loaded! Content length: {yandexResult.Length}");
}
Асинхронная модель на основе задач (TAP)
● Асинхронная модель на основе задач (Task-based Asynchronous Pattern, TAP).
● TAP основан на трех “китах” - типах Task и Task<T>, операторе await и маркере async.
● Task и Task<T> - описывают асинхронную операцию, ее результат (если есть) и callback-метод.
public class Task<TResult> : Task
{
public TResult Result { get; internal set; }
public Task<TNewResult> ContinueWith<TNewResult>(
Func<Task<TResult>, TNewResult> continuationFunction
);
}
● Маркер async применяется к методу, который возвращает Task, Task<T> или void, и когда в
теле метода используется оператор await.
● Оператор await нужен для объединения последовательности асинхронных операций в одну. Этот
оператор принимает на вход объект, описывающий асинхронную операцию, и так переписывает
код, что бы все, что следует после выражения с await, преобразовывалось в замыкание и было
аргументом метода ContinueWith объекта к которому применяется await.
Преобразованный код
[DebuggerStepThrough, AsyncStateMachine(typeof(Program.<MainAsync>d__1))]
public static void MainAsync()
{
Program.<MainAsync>d__1 <MainAsync>d__ = new Program.<MainAsync>d__1();
<MainAsync>d__.<>t__builder = AsyncVoidMethodBuilder.Create();
<MainAsync>d__.<>1__state = -1;
AsyncVoidMethodBuilder <>t__builder = <MainAsync>d__.<>t__builder;
<>t__builder.Start<Program.<MainAsync>d__1>(ref <MainAsync>d__);
}
[CompilerGenerated]
private sealed class <MainAsync>d__1 : IAsyncStateMachine
{
public int <>__state;
public AsyncVoidMethodBuilder <>t__builder;
private HttpClient <httpClient>5__1;
private string <googleResult>5__2;
private string <yandexResult>5__3;
private string <>s__4;
private string <>s__5;
private TaskAwaiter<string> <>u__1;
void IAsyncStateMachine.MoveNext()
{
int num1 = this.<>__state;
try
{
TaskAwaiter<string> awaiter1;
int num2;
TaskAwaiter<string> awaiter2;
if (num1 != 0)
{
if (num1 != 1)
{
this.<httpClient>5__1 = new HttpClient();
awaiter1 = this.<httpClient>5__1.GetStringAsync("https://google.com/").GetAwaiter();
if (!awaiter1.IsCompleted)
{
this.<>__state = num2 = 0;
this.<>u__1 = awaiter1;
Program.<MainAsync>d__1 stateMachine = this;
this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<string>,
Program.<MainAsync>d__1>(ref awaiter1, ref stateMachine);
return;
}
}
else
{
awaiter2 = this.<>u__1;
this.<>u__1 = new TaskAwaiter<string>();
this.<>__state = num2 = -1;
goto label_9;
}
}
else
else
{
awaiter1 = this.<>u__1;
this.<>u__1 = new TaskAwaiter<string>();
this.<>__state = num2 = -1;
}
string result1 = awaiter1.GetResult();
awaiter1 = new TaskAwaiter<string>();
this.<>s__4 = result1;
this.<googleResult>5__2 = this.<>s__4;
this.<>s__4 = (string) null;
Console.WriteLine(string.Format("Google loaded! Content length: {0}", (object) this.<googleResult>5__2.Length));
awaiter2 = this.<httpClient>5__1.GetStringAsync("https://yandex.ru").GetAwaiter();
if (!awaiter2.IsCompleted)
{
this.<>__state = num2 = 1;
this.<>u__1 = awaiter2;
Program.<MainAsync>d__1 stateMachine = this;
this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<string>, Program.<MainAsync>d__1>(ref awaiter2, ref stateMachine);
return;
}
label_9:
string result2 = awaiter2.GetResult();
awaiter2 = new TaskAwaiter<string>();
this.<>s__5 = result2;
this.<yandexResult>5__3 = this.<>s__5;
this.<>s__5 = (string) null;
Console.WriteLine(string.Format("Yandex loaded! Content length: {0}", (object) this.<yandexResult>5__3.Length));
}
catch (Exception ex)
{
this.<>__state = -2;
this.<>t__builder.SetException(ex);
return;
}
this.<>__state = -2;
this.<>t__builder.SetResult();
}
[DebuggerHidden]
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
{
}
}
Как работает async/await
private async Task LoadContentAsync(string url1, string url2)
{
if (url1 == null) throw new ArgumentNullException(nameof(url1));
if (url2 == null) throw new ArgumentNullException(nameof(url2));
string googleResult = await new HttpClient().GetStringAsync(url1); // 1
Console.WriteLine($"Url1 loaded! Content length: {googleResult.Length}");
string yandexResult = await new HttpClient().GetStringAsync(url2); // 2
Console.WriteLine($"Url2 loaded! Content length: {yandexResult.Length}");
}
● Часть, предшествующая “1” будет выполнена синхронно
● Затем в строке “1” будет запущена асинхронная операция, которой в качестве callback-а будет передан
оставшийся код метода. В этот момент поток, в котором выполнялся метод, будет возвращен в пул потоков
● После того, как операция “1” завершит работу, из пула будет извлечен поток, в нем будет вызван переданный
callback, и метод продолжит выполнение синхронно
● Затем в строке “2” будет запущена асинхронная операция, которой в качестве callback-а будет передан
оставшийся код метода. В этот момент поток, в котором выполнялся метод, будет возвращен в пул потоков
● После того, как операция “1” завершит работу, из пула потоков будет извлечен поток, в нем будет вызван
переданный callback, и метод продолжит выполнение
Когда использовать async/await
● в толстых клиентах (WPF, Xamarin), когда тяжелые операции запускаются асинхронно, не занимая UI поток
private async void btnRead_Click(object sender, EventArgs e)
{
btnRead.Enabled = false;
Content = await sr.ReadToEndAsync();
btnRead.Enabled = true;
}
Флаг Enabled в обоих случаях устанавливается UI-потоком. Раньше необходимо было писать так:
if (btnRead.InvokeRequired)
{
btnRead.Invoke((Action)(() => btnRead.Enabled = false));
}
else
{
btnRead.Enabled = false;
}
● в веб-приложениях (ASP.NET) для более эффективного использования потоков.
public class FileController
{
public async Task<string> GetFileAsync()
{
return await ReadFileAsync();
}
}
async/await в ASP.NET
Когда поступает запрос, ASP.NET берет один из потоков в своем пуле и закрепляет запрос за ним. Синхронный вызов внешнего
ресурса блокирует поток запроса, пока вызванный внешний ресурс не вернет управление.
При поступлении запроса в тот момент, когда все потоки из пула заняты, он будет вынужден ждать появления свободного потока,
прежде чем начнется его обработка, но запрос уже находится в системе. Его таймер тикает, и есть опасность возникновения HTTP
Error 503 (Service unavailable).
В случае асинхронного обработчика, когда поступает запрос, ASP.NET берет один из потоков в своем пуле и закрепляет его за
этим запросом. На этот раз обработчик вызовет внешний ресурс асинхронно. После этого поток запроса возвращается в пул до
тех пор, пока не произойдет возврат из вызова внешнего ресурса.
Task.ConfigureAwait(bool)
● Сообщает CLR, нужно ли при запуске асинхронной операции захватывать текущий контекст синхронизации или нет
var content = await ReadFileAsync().ConfigureAwait(false);
● Если после выполнения асинхронной операции необходимо продолжить выполнение в том же потоке, из которого
эта операция была запущена, то необходимо запускать операцию с параметром ConfigureAwait(true), либо вовсе
без ConfigureAwait.
Console.WriteLine($"ThreadId before: {Thread.CurrentThread.ManagedThreadId}");
var content = await new HttpClient().GetStringAsync("https://google.com");
Console.WriteLine($"ThreadId after: {Thread.CurrentThread.ManagedThreadId}");
Вывод:
ThreadId before: 2
ThreadId after: 2
● Если вернуться можно в любой поток, то необходимо использовать ConfigureAwait(false).
Console.WriteLine($"ThreadId before: {Thread.CurrentThread.ManagedThreadId}");
var content = await new HttpClient().GetStringAsync("https://google.com").ConfigureAwait(false);
Console.WriteLine($"ThreadId after: {Thread.CurrentThread.ManagedThreadId}");
Вывод:
ThreadId before: 2
ThreadId after: 9
Task.ConfigureAwait(bool) часть 2
● ConfigureAwait настраивает только “свою” асинхронную операцию
private async void btnRead_Click(object sender, EventArgs e)
{
var content = await GetContentAsync(); // 1
lbl_Content.Text = content;
}
private async Task<string> GetContentAsync()
{
var contentFromDb = await LoadFromDatabaseAsync().ConfigureAwait(false); // 2
return contentFromDb;
}
● При создании библиотек следует всегда использовать ConfigureAwait(false) за исключением случаев, когда эта
библиотека работает с UI или контекстом ASP.NET. Использование ConfigureAwait(false) дает ощутимый рост
производительности.
Асинхронная синхронность и наоборот
● Библиотека предоставляет несколько статических методов класса Task, предназначенных для создания Task’ов из
результатов синхронных вызовов - Task.FromResult, Task.FromException, Task.CompletedTask
static Task<string> SomeAsyncFunction()
{
string result = SomeSyncFunction();
return Task.FromResult(result);
}
static string SomeSyncFunction() => “string”
● Вызывать асинхронные методы из синхронных не рекомендуется, но если очень нужно, то можно использовать
Task.GetAwaiter().GetResult()
string content = new HttpClient().GetStringAsync("https://google.com").ConfigureAwait(false).GetAwaiter().GetResult();
string content = new HttpClient().GetStringAsync("https://google.com").ConfigureAwait(false).Result;
Соглашения
● Асинхронные методы должны всегда возвращать Task или Task<TResult> с единственным исключением -
обработчики событий могут возвращать void
○ private async void btnRead_Click(object sender, EventArgs e)
● Асинхронные методы имеют суффикс Async после имени операции
○ GetStreamAsync
● Если класс уже содержит метод с суффиксом Async, то вместо этого добавляется суффикс TaskAsync
○ GetStreamTaskAsync
● Параметры асинхронного метода должны соответствовать параметрам его синхронного аналога и должны
предоставляться в том же порядке.
○ public int Read(byte[] buffer,int offset,int count)
○ public Task<int> ReadAsync(byte[] buffer, int offset, int count)
● Out и ref параметры не поддерживаются, и должны возвращаться как часть результата TResult
○ public void GetTwoMaxValues(out int max1, out int max2)
○ public Task<(int max1, int max2)> GetTwoMaxValuesAsync()
● Если операция позволяет выполнить отмену, она предоставляет перезагрузку асинхронного метода, принимающую
токен отмены - параметр с именем cancellationToken.
○ public Task<int> ReadAsync(byte[] buffer, int offset, int count)
○ public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
Отмена асинхронной операции
● Для проверки факта отмены можно либо обращаться к свойству IsCancellationRequested, либо вызывать метод
ThrowIfCancellationRequested класса CancellationToken.
public static async Task FooAsync(CancellationToken cancellationToken)
{
while (true)
{
if (cancellationToken.IsCancellationRequested)
break;
// cancellationToken.ThrowIfCancellationRequested();
await Task.Delay(1000);
}
}
● Для отмены асинхронной операции перед ее вызовом создается объект CancellationTokenSource и в момент, когда
требуется отмена, вызывается его метод Cancel
public static async Task CallerAsync(CancellationToken cancellationToken)
{
CancellationTokenSource cts = new CancellationTokenSource();
await FooAsync(cts.Token);
cts.Cancel();
}
● Для вызова метода с “фиктивным” токеном отмены ему следует передать CancellationToken.None
await FooAsync(CancellationToken.None);
Отчет о ходе выполнения
● Для некоторых асинхронных операций имеет смысл предоставлять уведомления о ходе их выполнения, для этого
используется интерфейс IProgress<T>, который передается ей в качестве параметра
public interface IProgress<in T>
{
void Report(T value);
}
public static async Task FooAsync(IProgress<int> progress)
{
for (int i = 0; i < 100; ++i)
{
if (i % 10 == 0) progress?.Report(i);
await Task.Delay(1000);
}
}
● Операция должна допускать значение null - в этом случае о ходе выполнения не сообщается.
● .NET Framework предоставляет одну реализацию - Progress<T>, который при вызове Report использует контекст
синхронизации, активный в момент его создания. Это позволяет, например, безопасно обновлять элементы UI
public async void Button1_Click(object sender, EventArgs args)
{
var progress = new Progress<int>(value =>
{
label2.Text = value.ToString();
});
await FooAsync(progress);
}
Await’ить или Task’овать
public static async Task CallerAsyncAsync()
{
var task = SomeAsyncFunction(null); // 1
Console.WriteLine("ByndyuSoft are the best!");
await task; // 2
}
● Вариант с возвратом Task
static Task<string> SomeAsyncFunction(string uri)
{
if (uri == null) throw new ArgumentNullException(nameof(uri));
return new HttpClient().GetStringAsync(uri);
}
Исключение будет возбуждено строке “1”
● Вариант с await
static async Task<string> SomeAsyncFunction(string uri)
{
if (uri == null) throw new ArgumentNullException(nameof(uri));
return await new HttpClient().GetStringAsync(uri);
}
Исключение будет возбуждено строке “2”

More Related Content

What's hot (20)

Service Discovery. More that it seems
Service Discovery. More that it seemsService Discovery. More that it seems
Service Discovery. More that it seems
Aleksandr Tarasov
Превышаем скоростные лимиты с Angular 2
Превышаем скоростные лимиты с Angular 2Превышаем скоростные лимиты с Angular 2
Превышаем скоростные лимиты с Angular 2
Oleksii Okhrymenko
Производительность open source решений
Производительность open source решенийПроизводительность open source решений
Производительность open source решений
Vladimir Sitnikov
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Noveo
Михаил Давыдов - JavaScript. Асинхронность
Михаил Давыдов - JavaScript. АсинхронностьМихаил Давыдов - JavaScript. Асинхронность
Михаил Давыдов - JavaScript. Асинхронность
Yandex
Михаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: АсинхронностьМихаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: Асинхронность
Yandex
Реактивный двигатель вашего Android приложения
Реактивный двигатель вашего Android приложенияРеактивный двигатель вашего Android приложения
Реактивный двигатель вашего Android приложения
Matvey Malkov
Эволюционный дизайн. Joker Students Day 2016
Эволюционный дизайн. Joker Students Day 2016Эволюционный дизайн. Joker Students Day 2016
Эволюционный дизайн. Joker Students Day 2016
Кирилл Толкачёв
RxJava + Retrofit
RxJava + RetrofitRxJava + Retrofit
RxJava + Retrofit
Dev2Dev
Коротко о React.js
Коротко о React.jsКоротко о React.js
Коротко о React.js
Mad Devs
Пользователь точно оценит! Повышение производительности мобильных приложений ...
Пользователь точно оценит! Повышение производительности мобильных приложений ...Пользователь точно оценит! Повышение производительности мобильных приложений ...
Пользователь точно оценит! Повышение производительности мобильных приложений ...
Ontico
Android - 11 - Multithreading
Android - 11 - MultithreadingAndroid - 11 - Multithreading
Android - 11 - Multithreading
Noveo
Транзакционный фреймворк для сингловых игр и игр с асинхронным мультиплеером ...
Транзакционный фреймворк для сингловых игр и игр с асинхронным мультиплеером ...Транзакционный фреймворк для сингловых игр и игр с асинхронным мультиплеером ...
Транзакционный фреймворк для сингловых игр и игр с асинхронным мультиплеером ...
DevGAMM Conference
"Пиринговый веб на JavaScript"
"Пиринговый веб на JavaScript""Пиринговый веб на JavaScript"
"Пиринговый веб на JavaScript"
FDConf
How to build solid CI-CD pipeline / Илья Беда (beda.software)
How to build solid CI-CD pipeline / Илья Беда (beda.software)How to build solid CI-CD pipeline / Илья Беда (beda.software)
How to build solid CI-CD pipeline / Илья Беда (beda.software)
Ontico
Практика Lock-free. RealTime-сервер
Практика Lock-free. RealTime-серверПрактика Lock-free. RealTime-сервер
Практика Lock-free. RealTime-сервер
Platonov Sergey
PostgreSQL Vacuum: Nine Circles of Hell
PostgreSQL Vacuum: Nine Circles of HellPostgreSQL Vacuum: Nine Circles of Hell
PostgreSQL Vacuum: Nine Circles of Hell
Alexey Lesovsky
«Continuous Integration — A to Z или Непрерывная интеграция — кто всё сломал?»
«Continuous Integration — A to Z или Непрерывная интеграция — кто всё сломал?»«Continuous Integration — A to Z или Непрерывная интеграция — кто всё сломал?»
«Continuous Integration — A to Z или Непрерывная интеграция — кто всё сломал?»
FDConf
JavaDay'14
JavaDay'14JavaDay'14
JavaDay'14
Kirill Golodnov
Service Discovery. More that it seems
Service Discovery. More that it seemsService Discovery. More that it seems
Service Discovery. More that it seems
Aleksandr Tarasov
Превышаем скоростные лимиты с Angular 2
Превышаем скоростные лимиты с Angular 2Превышаем скоростные лимиты с Angular 2
Превышаем скоростные лимиты с Angular 2
Oleksii Okhrymenko
Производительность open source решений
Производительность open source решенийПроизводительность open source решений
Производительность open source решений
Vladimir Sitnikov
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Noveo
Михаил Давыдов - JavaScript. Асинхронность
Михаил Давыдов - JavaScript. АсинхронностьМихаил Давыдов - JavaScript. Асинхронность
Михаил Давыдов - JavaScript. Асинхронность
Yandex
Михаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: АсинхронностьМихаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: Асинхронность
Yandex
Реактивный двигатель вашего Android приложения
Реактивный двигатель вашего Android приложенияРеактивный двигатель вашего Android приложения
Реактивный двигатель вашего Android приложения
Matvey Malkov
Эволюционный дизайн. Joker Students Day 2016
Эволюционный дизайн. Joker Students Day 2016Эволюционный дизайн. Joker Students Day 2016
Эволюционный дизайн. Joker Students Day 2016
Кирилл Толкачёв
RxJava + Retrofit
RxJava + RetrofitRxJava + Retrofit
RxJava + Retrofit
Dev2Dev
Коротко о React.js
Коротко о React.jsКоротко о React.js
Коротко о React.js
Mad Devs
Пользователь точно оценит! Повышение производительности мобильных приложений ...
Пользователь точно оценит! Повышение производительности мобильных приложений ...Пользователь точно оценит! Повышение производительности мобильных приложений ...
Пользователь точно оценит! Повышение производительности мобильных приложений ...
Ontico
Android - 11 - Multithreading
Android - 11 - MultithreadingAndroid - 11 - Multithreading
Android - 11 - Multithreading
Noveo
Транзакционный фреймворк для сингловых игр и игр с асинхронным мультиплеером ...
Транзакционный фреймворк для сингловых игр и игр с асинхронным мультиплеером ...Транзакционный фреймворк для сингловых игр и игр с асинхронным мультиплеером ...
Транзакционный фреймворк для сингловых игр и игр с асинхронным мультиплеером ...
DevGAMM Conference
"Пиринговый веб на JavaScript"
"Пиринговый веб на JavaScript""Пиринговый веб на JavaScript"
"Пиринговый веб на JavaScript"
FDConf
How to build solid CI-CD pipeline / Илья Беда (beda.software)
How to build solid CI-CD pipeline / Илья Беда (beda.software)How to build solid CI-CD pipeline / Илья Беда (beda.software)
How to build solid CI-CD pipeline / Илья Беда (beda.software)
Ontico
Практика Lock-free. RealTime-сервер
Практика Lock-free. RealTime-серверПрактика Lock-free. RealTime-сервер
Практика Lock-free. RealTime-сервер
Platonov Sergey
PostgreSQL Vacuum: Nine Circles of Hell
PostgreSQL Vacuum: Nine Circles of HellPostgreSQL Vacuum: Nine Circles of Hell
PostgreSQL Vacuum: Nine Circles of Hell
Alexey Lesovsky
«Continuous Integration — A to Z или Непрерывная интеграция — кто всё сломал?»
«Continuous Integration — A to Z или Непрерывная интеграция — кто всё сломал?»«Continuous Integration — A to Z или Непрерывная интеграция — кто всё сломал?»
«Continuous Integration — A to Z или Непрерывная интеграция — кто всё сломал?»
FDConf

Similar to Киллер-фича языка C# — конструкция async/await (20)

javaaaaddawdawdawdasdsadsaddadadm11n.pptx
javaaaaddawdawdawdasdsadsaddadadm11n.pptxjavaaaaddawdawdawdasdsadsaddadadm11n.pptx
javaaaaddawdawdawdasdsadsaddadadm11n.pptx
ssuserb46e0b
Luxoft async.net
Luxoft async.netLuxoft async.net
Luxoft async.net
Sergey Teplyakov
Async clinic by by Sergey Teplyakov
Async clinic by by Sergey TeplyakovAsync clinic by by Sergey Teplyakov
Async clinic by by Sergey Teplyakov
Alex Tumanoff
Асинхронность и сопрограммы
Асинхронность и сопрограммыАсинхронность и сопрограммы
Асинхронность и сопрограммы
Platonov Sergey
Java весна 2014 лекция 5
Java весна 2014 лекция 5Java весна 2014 лекция 5
Java весна 2014 лекция 5
Technopark
Mike ponomarenko java17-fork-v1.2
Mike ponomarenko java17-fork-v1.2Mike ponomarenko java17-fork-v1.2
Mike ponomarenko java17-fork-v1.2
Alex Tumanoff
Григорий Демченко — Асинхронное программирование и сопрограммы
Григорий Демченко — Асинхронное программирование и сопрограммыГригорий Демченко — Асинхронное программирование и сопрограммы
Григорий Демченко — Асинхронное программирование и сопрограммы
Yandex
Asynchrony and coroutines
Asynchrony and coroutinesAsynchrony and coroutines
Asynchrony and coroutines
corehard_by
Android осень 2013 лекция 4
Android осень 2013 лекция 4Android осень 2013 лекция 4
Android осень 2013 лекция 4
Technopark
Thread
ThreadThread
Thread
Alexander Rusin
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
Омские ИТ-субботники
JPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerJPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profiler
Anton Arhipov
Веселая ферма. Соседи.
Веселая ферма. Соседи.Веселая ферма. Соседи.
Веселая ферма. Соседи.
Doomer Samoiloff
Ilia kantor паттерны серверных comet решений
Ilia kantor паттерны серверных comet решенийIlia kantor паттерны серверных comet решений
Ilia kantor паттерны серверных comet решений
rit2010
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Andrey Rebrov
Javascript 1
Javascript 1Javascript 1
Javascript 1
Andrey Dolinin
async/await: собираем грабли
async/await: собираем граблиasync/await: собираем грабли
async/await: собираем грабли
Andrey Chasovskikh
javaaaaddawdawdawdasdsadsaddadadm11n.pptx
javaaaaddawdawdawdasdsadsaddadadm11n.pptxjavaaaaddawdawdawdasdsadsaddadadm11n.pptx
javaaaaddawdawdawdasdsadsaddadadm11n.pptx
ssuserb46e0b
Async clinic by by Sergey Teplyakov
Async clinic by by Sergey TeplyakovAsync clinic by by Sergey Teplyakov
Async clinic by by Sergey Teplyakov
Alex Tumanoff
Асинхронность и сопрограммы
Асинхронность и сопрограммыАсинхронность и сопрограммы
Асинхронность и сопрограммы
Platonov Sergey
Java весна 2014 лекция 5
Java весна 2014 лекция 5Java весна 2014 лекция 5
Java весна 2014 лекция 5
Technopark
Mike ponomarenko java17-fork-v1.2
Mike ponomarenko java17-fork-v1.2Mike ponomarenko java17-fork-v1.2
Mike ponomarenko java17-fork-v1.2
Alex Tumanoff
Григорий Демченко — Асинхронное программирование и сопрограммы
Григорий Демченко — Асинхронное программирование и сопрограммыГригорий Демченко — Асинхронное программирование и сопрограммы
Григорий Демченко — Асинхронное программирование и сопрограммы
Yandex
Asynchrony and coroutines
Asynchrony and coroutinesAsynchrony and coroutines
Asynchrony and coroutines
corehard_by
Android осень 2013 лекция 4
Android осень 2013 лекция 4Android осень 2013 лекция 4
Android осень 2013 лекция 4
Technopark
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
Омские ИТ-субботники
JPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerJPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profiler
Anton Arhipov
Веселая ферма. Соседи.
Веселая ферма. Соседи.Веселая ферма. Соседи.
Веселая ферма. Соседи.
Doomer Samoiloff
Ilia kantor паттерны серверных comet решений
Ilia kantor паттерны серверных comet решенийIlia kantor паттерны серверных comet решений
Ilia kantor паттерны серверных comet решений
rit2010
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Andrey Rebrov
async/await: собираем грабли
async/await: собираем граблиasync/await: собираем грабли
async/await: собираем грабли
Andrey Chasovskikh

Киллер-фича языка C# — конструкция async/await

  • 2. Синхронный метод static void Main(string[] args) { string googleResult = new HttpClient().GetString("https://google.com/"); // 1 Console.WriteLine($"Google loaded! Content length: {googleResult.Length}"); string yandexResult = new HttpClient().GetString("https://yandex.ru"); // 2 Console.WriteLine($"Yandex loaded! Content length: {yandexResult.Length}"); }
  • 3. Асинхронный метод (.NET 4.0) static Task MainAsync() { return new HttpClient().GetStringAsync("https://google.com") .ContinueWith(task => { Console.WriteLine($"Google loaded! Content length: {task.Result.Length}"); return new HttpClient().GetStringAsync("https://yandex.ru"); }).Unwrap() // Task<Task<string>> => Task<string> .ContinueWith(task => { Console.WriteLine($"Yandex loaded! Content length: {task.Result.Length}"); }); }
  • 4. Асинхронный метод (.NET 4.5) public static async Task MainAsync() { string googleResult = await new HttpClient().GetStringAsync("https://google.com/"); Console.WriteLine($"Google loaded! Content length: {googleResult.Length}"); string yandexResult = await new HttpClient().GetStringAsync("https://yandex.ru"); Console.WriteLine($"Yandex loaded! Content length: {yandexResult.Length}"); }
  • 5. Асинхронная модель на основе задач (TAP) ● Асинхронная модель на основе задач (Task-based Asynchronous Pattern, TAP). ● TAP основан на трех “китах” - типах Task и Task<T>, операторе await и маркере async. ● Task и Task<T> - описывают асинхронную операцию, ее результат (если есть) и callback-метод. public class Task<TResult> : Task { public TResult Result { get; internal set; } public Task<TNewResult> ContinueWith<TNewResult>( Func<Task<TResult>, TNewResult> continuationFunction ); } ● Маркер async применяется к методу, который возвращает Task, Task<T> или void, и когда в теле метода используется оператор await. ● Оператор await нужен для объединения последовательности асинхронных операций в одну. Этот оператор принимает на вход объект, описывающий асинхронную операцию, и так переписывает код, что бы все, что следует после выражения с await, преобразовывалось в замыкание и было аргументом метода ContinueWith объекта к которому применяется await.
  • 6. Преобразованный код [DebuggerStepThrough, AsyncStateMachine(typeof(Program.<MainAsync>d__1))] public static void MainAsync() { Program.<MainAsync>d__1 <MainAsync>d__ = new Program.<MainAsync>d__1(); <MainAsync>d__.<>t__builder = AsyncVoidMethodBuilder.Create(); <MainAsync>d__.<>1__state = -1; AsyncVoidMethodBuilder <>t__builder = <MainAsync>d__.<>t__builder; <>t__builder.Start<Program.<MainAsync>d__1>(ref <MainAsync>d__); }
  • 7. [CompilerGenerated] private sealed class <MainAsync>d__1 : IAsyncStateMachine { public int <>__state; public AsyncVoidMethodBuilder <>t__builder; private HttpClient <httpClient>5__1; private string <googleResult>5__2; private string <yandexResult>5__3; private string <>s__4; private string <>s__5; private TaskAwaiter<string> <>u__1; void IAsyncStateMachine.MoveNext() { int num1 = this.<>__state; try { TaskAwaiter<string> awaiter1; int num2; TaskAwaiter<string> awaiter2; if (num1 != 0) { if (num1 != 1) { this.<httpClient>5__1 = new HttpClient(); awaiter1 = this.<httpClient>5__1.GetStringAsync("https://google.com/").GetAwaiter(); if (!awaiter1.IsCompleted) { this.<>__state = num2 = 0; this.<>u__1 = awaiter1; Program.<MainAsync>d__1 stateMachine = this; this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<string>, Program.<MainAsync>d__1>(ref awaiter1, ref stateMachine); return; } } else { awaiter2 = this.<>u__1; this.<>u__1 = new TaskAwaiter<string>(); this.<>__state = num2 = -1; goto label_9; } } else else { awaiter1 = this.<>u__1; this.<>u__1 = new TaskAwaiter<string>(); this.<>__state = num2 = -1; } string result1 = awaiter1.GetResult(); awaiter1 = new TaskAwaiter<string>(); this.<>s__4 = result1; this.<googleResult>5__2 = this.<>s__4; this.<>s__4 = (string) null; Console.WriteLine(string.Format("Google loaded! Content length: {0}", (object) this.<googleResult>5__2.Length)); awaiter2 = this.<httpClient>5__1.GetStringAsync("https://yandex.ru").GetAwaiter(); if (!awaiter2.IsCompleted) { this.<>__state = num2 = 1; this.<>u__1 = awaiter2; Program.<MainAsync>d__1 stateMachine = this; this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<string>, Program.<MainAsync>d__1>(ref awaiter2, ref stateMachine); return; } label_9: string result2 = awaiter2.GetResult(); awaiter2 = new TaskAwaiter<string>(); this.<>s__5 = result2; this.<yandexResult>5__3 = this.<>s__5; this.<>s__5 = (string) null; Console.WriteLine(string.Format("Yandex loaded! Content length: {0}", (object) this.<yandexResult>5__3.Length)); } catch (Exception ex) { this.<>__state = -2; this.<>t__builder.SetException(ex); return; } this.<>__state = -2; this.<>t__builder.SetResult(); } [DebuggerHidden] void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { } }
  • 8. Как работает async/await private async Task LoadContentAsync(string url1, string url2) { if (url1 == null) throw new ArgumentNullException(nameof(url1)); if (url2 == null) throw new ArgumentNullException(nameof(url2)); string googleResult = await new HttpClient().GetStringAsync(url1); // 1 Console.WriteLine($"Url1 loaded! Content length: {googleResult.Length}"); string yandexResult = await new HttpClient().GetStringAsync(url2); // 2 Console.WriteLine($"Url2 loaded! Content length: {yandexResult.Length}"); } ● Часть, предшествующая “1” будет выполнена синхронно ● Затем в строке “1” будет запущена асинхронная операция, которой в качестве callback-а будет передан оставшийся код метода. В этот момент поток, в котором выполнялся метод, будет возвращен в пул потоков ● После того, как операция “1” завершит работу, из пула будет извлечен поток, в нем будет вызван переданный callback, и метод продолжит выполнение синхронно ● Затем в строке “2” будет запущена асинхронная операция, которой в качестве callback-а будет передан оставшийся код метода. В этот момент поток, в котором выполнялся метод, будет возвращен в пул потоков ● После того, как операция “1” завершит работу, из пула потоков будет извлечен поток, в нем будет вызван переданный callback, и метод продолжит выполнение
  • 9. Когда использовать async/await ● в толстых клиентах (WPF, Xamarin), когда тяжелые операции запускаются асинхронно, не занимая UI поток private async void btnRead_Click(object sender, EventArgs e) { btnRead.Enabled = false; Content = await sr.ReadToEndAsync(); btnRead.Enabled = true; } Флаг Enabled в обоих случаях устанавливается UI-потоком. Раньше необходимо было писать так: if (btnRead.InvokeRequired) { btnRead.Invoke((Action)(() => btnRead.Enabled = false)); } else { btnRead.Enabled = false; } ● в веб-приложениях (ASP.NET) для более эффективного использования потоков. public class FileController { public async Task<string> GetFileAsync() { return await ReadFileAsync(); } }
  • 10. async/await в ASP.NET Когда поступает запрос, ASP.NET берет один из потоков в своем пуле и закрепляет запрос за ним. Синхронный вызов внешнего ресурса блокирует поток запроса, пока вызванный внешний ресурс не вернет управление. При поступлении запроса в тот момент, когда все потоки из пула заняты, он будет вынужден ждать появления свободного потока, прежде чем начнется его обработка, но запрос уже находится в системе. Его таймер тикает, и есть опасность возникновения HTTP Error 503 (Service unavailable). В случае асинхронного обработчика, когда поступает запрос, ASP.NET берет один из потоков в своем пуле и закрепляет его за этим запросом. На этот раз обработчик вызовет внешний ресурс асинхронно. После этого поток запроса возвращается в пул до тех пор, пока не произойдет возврат из вызова внешнего ресурса.
  • 11. Task.ConfigureAwait(bool) ● Сообщает CLR, нужно ли при запуске асинхронной операции захватывать текущий контекст синхронизации или нет var content = await ReadFileAsync().ConfigureAwait(false); ● Если после выполнения асинхронной операции необходимо продолжить выполнение в том же потоке, из которого эта операция была запущена, то необходимо запускать операцию с параметром ConfigureAwait(true), либо вовсе без ConfigureAwait. Console.WriteLine($"ThreadId before: {Thread.CurrentThread.ManagedThreadId}"); var content = await new HttpClient().GetStringAsync("https://google.com"); Console.WriteLine($"ThreadId after: {Thread.CurrentThread.ManagedThreadId}"); Вывод: ThreadId before: 2 ThreadId after: 2 ● Если вернуться можно в любой поток, то необходимо использовать ConfigureAwait(false). Console.WriteLine($"ThreadId before: {Thread.CurrentThread.ManagedThreadId}"); var content = await new HttpClient().GetStringAsync("https://google.com").ConfigureAwait(false); Console.WriteLine($"ThreadId after: {Thread.CurrentThread.ManagedThreadId}"); Вывод: ThreadId before: 2 ThreadId after: 9
  • 12. Task.ConfigureAwait(bool) часть 2 ● ConfigureAwait настраивает только “свою” асинхронную операцию private async void btnRead_Click(object sender, EventArgs e) { var content = await GetContentAsync(); // 1 lbl_Content.Text = content; } private async Task<string> GetContentAsync() { var contentFromDb = await LoadFromDatabaseAsync().ConfigureAwait(false); // 2 return contentFromDb; } ● При создании библиотек следует всегда использовать ConfigureAwait(false) за исключением случаев, когда эта библиотека работает с UI или контекстом ASP.NET. Использование ConfigureAwait(false) дает ощутимый рост производительности.
  • 13. Асинхронная синхронность и наоборот ● Библиотека предоставляет несколько статических методов класса Task, предназначенных для создания Task’ов из результатов синхронных вызовов - Task.FromResult, Task.FromException, Task.CompletedTask static Task<string> SomeAsyncFunction() { string result = SomeSyncFunction(); return Task.FromResult(result); } static string SomeSyncFunction() => “string” ● Вызывать асинхронные методы из синхронных не рекомендуется, но если очень нужно, то можно использовать Task.GetAwaiter().GetResult() string content = new HttpClient().GetStringAsync("https://google.com").ConfigureAwait(false).GetAwaiter().GetResult(); string content = new HttpClient().GetStringAsync("https://google.com").ConfigureAwait(false).Result;
  • 14. Соглашения ● Асинхронные методы должны всегда возвращать Task или Task<TResult> с единственным исключением - обработчики событий могут возвращать void ○ private async void btnRead_Click(object sender, EventArgs e) ● Асинхронные методы имеют суффикс Async после имени операции ○ GetStreamAsync ● Если класс уже содержит метод с суффиксом Async, то вместо этого добавляется суффикс TaskAsync ○ GetStreamTaskAsync ● Параметры асинхронного метода должны соответствовать параметрам его синхронного аналога и должны предоставляться в том же порядке. ○ public int Read(byte[] buffer,int offset,int count) ○ public Task<int> ReadAsync(byte[] buffer, int offset, int count) ● Out и ref параметры не поддерживаются, и должны возвращаться как часть результата TResult ○ public void GetTwoMaxValues(out int max1, out int max2) ○ public Task<(int max1, int max2)> GetTwoMaxValuesAsync() ● Если операция позволяет выполнить отмену, она предоставляет перезагрузку асинхронного метода, принимающую токен отмены - параметр с именем cancellationToken. ○ public Task<int> ReadAsync(byte[] buffer, int offset, int count) ○ public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
  • 15. Отмена асинхронной операции ● Для проверки факта отмены можно либо обращаться к свойству IsCancellationRequested, либо вызывать метод ThrowIfCancellationRequested класса CancellationToken. public static async Task FooAsync(CancellationToken cancellationToken) { while (true) { if (cancellationToken.IsCancellationRequested) break; // cancellationToken.ThrowIfCancellationRequested(); await Task.Delay(1000); } } ● Для отмены асинхронной операции перед ее вызовом создается объект CancellationTokenSource и в момент, когда требуется отмена, вызывается его метод Cancel public static async Task CallerAsync(CancellationToken cancellationToken) { CancellationTokenSource cts = new CancellationTokenSource(); await FooAsync(cts.Token); cts.Cancel(); } ● Для вызова метода с “фиктивным” токеном отмены ему следует передать CancellationToken.None await FooAsync(CancellationToken.None);
  • 16. Отчет о ходе выполнения ● Для некоторых асинхронных операций имеет смысл предоставлять уведомления о ходе их выполнения, для этого используется интерфейс IProgress<T>, который передается ей в качестве параметра public interface IProgress<in T> { void Report(T value); } public static async Task FooAsync(IProgress<int> progress) { for (int i = 0; i < 100; ++i) { if (i % 10 == 0) progress?.Report(i); await Task.Delay(1000); } } ● Операция должна допускать значение null - в этом случае о ходе выполнения не сообщается. ● .NET Framework предоставляет одну реализацию - Progress<T>, который при вызове Report использует контекст синхронизации, активный в момент его создания. Это позволяет, например, безопасно обновлять элементы UI public async void Button1_Click(object sender, EventArgs args) { var progress = new Progress<int>(value => { label2.Text = value.ToString(); }); await FooAsync(progress); }
  • 17. Await’ить или Task’овать public static async Task CallerAsyncAsync() { var task = SomeAsyncFunction(null); // 1 Console.WriteLine("ByndyuSoft are the best!"); await task; // 2 } ● Вариант с возвратом Task static Task<string> SomeAsyncFunction(string uri) { if (uri == null) throw new ArgumentNullException(nameof(uri)); return new HttpClient().GetStringAsync(uri); } Исключение будет возбуждено строке “1” ● Вариант с await static async Task<string> SomeAsyncFunction(string uri) { if (uri == null) throw new ArgumentNullException(nameof(uri)); return await new HttpClient().GetStringAsync(uri); } Исключение будет возбуждено строке “2”