:: Не фоксом единым
Асинхронность
S-type

Сообщений: 2969
Дата регистрации: 24.04.2004
Есть Web-сервис, на C#, работает под IIS. Возникли проблемы - понадобилось выводить в файл (для отсылки в стороннюю поддержку) данные. Реализовал примерно так:

static void myFunc(byte[] bytes)
{
try
{
using (FileStream fileStream = new FileStream(@"c:\temp\sss.txt", FileMode.Create, FileAccess.Write))
{
fileStream.Write(bytes, 0, bytes.Length);
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message); // в реальной программе тут стоит запись в лог
}
}
static void Main(string[] args)
{
// что то делается
byte[] buffer = Encoding.UTF8.GetBytes("Данные");
myFunc(buffer); // то, что вставил, что бы сохранять промежуточные данные
// что то ещё делается
Console.ReadLine();
}

Т.е. программа доходит до myFunc, выполняет её, и движется дальше. Всё работает, претензий ко мне нет. Но меня грызёт червь перфекционизма - можно же сделать лучше, можно же сделать асинхронно.

Указал

fileStream.WriteAsync(bytes, 0, bytes.Length);

но этого явно не достаточно. Что ещё надо доделать?



Исправлено 3 раз(а). Последнее : S-type, 26.05.17 16:15
Ratings: 0 negative/0 positive
Re: Асинхронность
S-type

Сообщений: 2969
Дата регистрации: 24.04.2004
Вот так удалось заставить работать:

static async void myFuncAsync(byte[] bytes)
{
try
{
using (FileStream fileStream = new FileStream(@"c:\temp\sss.txt", FileMode.Create, FileAccess.Write))
{
await fileStream.WriteAsync(bytes, 0, bytes.Length);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message); // в реальной программе тут стоит запись в лог
}
}

Это корректный код?
Ratings: 0 negative/0 positive
Re: Асинхронность
Igor Korolyov
Автор

Сообщений: 34580
Дата регистрации: 28.05.2002
S-type
Это корректный код?
Да, насколько я понимаю


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Асинхронность
S-type

Сообщений: 2969
Дата регистрации: 24.04.2004
Это будет моя первая асинхронная функция... В понедельник поставлю "на бой".
Ratings: 0 negative/0 positive
Re: Асинхронность
S-type

Сообщений: 2969
Дата регистрации: 24.04.2004
Всё работает, вот теперь только осталось разобраться - как. Точнее - с какого именно места начинается асинхронность? С момента вызова функции myFuncAsync (функция опИсана как async), или с момента вызова fileStream.WriteAsync (вызов с помощью await)?



Исправлено 1 раз(а). Последнее : S-type, 27.05.17 17:29
Ratings: 0 negative/0 positive
Re: Асинхронность
S-type

Сообщений: 2969
Дата регистрации: 24.04.2004
На сколько понимаю, можно сделать так:

static async void myFuncAsync(byte[] bytes)
{
using (FileStream fileStream = new FileStream(@"c:\temp\sss.txt", FileMode.Create, FileAccess.Write))
{
Task task = fileStream.WriteAsync(bytes, 0, bytes.Length).ContinueWith
(result => { Console.WriteLine("Сохранение завершено"); });
Console.WriteLine("Ещё какие то действия");
await task;
Console.WriteLine("Продолжаем");
}
}

выведет:

Ещё какие то действия
Сохранение завершено
Продолжаем

Выходит, асинхронность начинается с вызова fileStream.WriteAsync. Тогда совсем не понятен смысл - зачем объявлять функцию ключевым словом async. Компилятор без него функцию не даёт откомпилировать. В чём смысл?



Исправлено 2 раз(а). Последнее : S-type, 27.05.17 18:28
Ratings: 0 negative/0 positive
Re: Асинхронность
Igor Korolyov
Автор

Сообщений: 34580
Дата регистрации: 28.05.2002
Вот это изучить - зачитать до дыр
Асинхронность "начинается" с использования async в определении метода. Одного этого уже достаточно, чтобы система скомпилировала данный метод как state-машину (а не как простой, ординарный метод). И, если возвращаемый тип метода будет Task или Task<T>, то ЕГО уже можно будет "вызывать асинхронно".

await внутри тела такого метода задаёт "точки приостановки". Т.е. по сути делит метод на части - каждая из которых будет скомпилирована в отдельный "шаг" state-машины. Если await нет, то и точек приостановки нет, и никакой реальной асинхронности не получится.

Самая первая из них определяет (грубо говоря) где метод начнёт работать асинхронно - а точнее где метод вернёт управление в вызывающий код, передав тому объект Task (или его generic версию, если метод должен вернуть какое-то значение). Этот объект Task служит для того чтобы вызывающий метод мог спокойно продолжать работать, а в нужный момент времени мог (если это необходимо) дождаться завершения асинхронного метода и получить его возвращаемое значение.

По твоему коду:
Нужно совершенно чётко представлять, что из метода myFuncAsync ты "вывалишься" в вызывающий метод вовсе не после записи файла и печатания "Сохранение завершено", а ПЕРЕД этим - сразу же после Console.WriteLine("Ещё какие то действия");
А строка "Продолжаем" может и вовсе не вывестись - например если вызов myFuncAsync стоит сразу же в Main, а после него нет ничего полезного, или занимающего время ожидания (типа того же Console.ReadKey())
По хорошему этот метод должен возвращать тип Task, а в вызывающем его методе (не async - тот же Main в тесте) нужно применять нечто типа
var task = myFuncAsync(bytesToWrite);
...
task.Wait();

Тогда хотя-бы есть гарантия что мы дождёмся завершения его работы. Иначе - ну может так произойти, что процесс завершится ещё до того как завершились все подобные "асинхронные" методы. Сам то процесс записи мне не удалось "прервать", а вот добиться того что не печатается "Продолжаем" и не создаётся файлик-флажок простейшим кодом
File.WriteAllText(@"D:\sss.log", "OK");
запросто - достаточно было раздуть записываемый массив байт до пары Мб.
Даже при размере записываемого массива "всего" в 10кб значительная часть запусков проходила так, что "продолжения" на которое поделил await метод myFuncAsync не успевало сработать...

Как я понимаю, в твоём изначальном примере такой проблемы нет, т.к. там нет ничего после await - никаких "продолжений"... Но я не силён в этих новых фишках, потому и написал более осторожно без гарантии того что это абсолютно правильный код
Всё же более безопасно, я полагаю, сделать этот myFuncAsync возвращающим Task, а в Main таки гарантированно дожидаться его завершения. Именно по выше изложенной схеме с task.Wait(); т.к. сам Main нельзя делать async
А то ещё и с проверкой статуса отработки Task-а, и отловом потенциально возникающих в нём исключений (если он не ловит и не давит ВСЕ возможные исключения, как у тебя в начальном примере - ну и если твоя "запись в лог" сама по себе не может выкинуть исключения, что тоже бывает в реальной жизни ).


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Асинхронность
S-type

Сообщений: 2969
Дата регистрации: 24.04.2004
Igor Korolyov
А строка "Продолжаем" может и вовсе не вывестись - например если вызов myFuncAsync стоит сразу же в Main, а после него нет ничего полезного, или занимающего время ожидания (типа того же Console.ReadKey())
И действительно... Если убрать ReadLine и получится такой вот код:



Программа отработает, файл sss.txt создаётся, но данных в файле нет - файл пустой! Т.е. всё, что после await (включая сохранение в файл) не успевает отработать!

Igor Korolyov
По хорошему этот метод должен возвращать тип Task, а в вызывающем его методе (не async - тот же Main в тесте) нужно применять нечто типа
var task = myFuncAsync(bytesToWrite);
...
task.Wait();


Выходит, правильный код такой:

static async Task myFuncAsync(byte[] bytes)
{
using (FileStream fileStream = new FileStream(@"c:\temp\sss.txt", FileMode.Create, FileAccess.Write))
{
await fileStream.WriteAsync(bytes, 0, bytes.Length);
}
}
static void Main(string[] args)
{
byte[] buffer = Encoding.UTF8.GetBytes("Данные");
Task task = myFuncAsync(buffer);
// тут ещё какие то действия
task.Wait();
}



Исправлено 3 раз(а). Последнее : S-type, 28.05.17 20:53
Ratings: 0 negative/0 positive
Re: Асинхронность
S-type

Сообщений: 2969
Дата регистрации: 24.04.2004
Спасибо. Это очень существенное дополнение. Полез читать теорию...
Ratings: 0 negative/0 positive
Re: Асинхронность
S-type

Сообщений: 2969
Дата регистрации: 24.04.2004
Igor Korolyov
запросто - достаточно было раздуть записываемый массив байт до пары Мб.
Даже при размере записываемого массива "всего" в 10кб значительная часть запусков проходила так, что "продолжения" на которое поделил await метод myFuncAsync не успевало сработать...

То есть это ещё один способ - как незаметно отстрелить себе ногу (как сделать глючный код).
Ratings: 0 negative/0 positive
Re: Асинхронность
Igor Korolyov
Автор

Сообщений: 34580
Дата регистрации: 28.05.2002
Да. Но справедливости ради - соорудить КОРРЕКТНЫЙ асинхронный код без подобных "упрощателей" сможет очень небольшое число разработчиков. А так - посреди работающей программы даже не совсем аккуратное обращение с асинхронными методами вряд ли приведёт к катастрофическим последствиям


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Асинхронность
Igor Korolyov
Автор

Сообщений: 34580
Дата регистрации: 28.05.2002
Спросил у коллег - так "с возмущением" сказали что выгнали б с собеседования того кто применяет async void а не async Task

Хотя и сами не могут точно ответить на кучу вопросов возникающих при, скажем так, не совсем аккуратном использовании многопоточности вообще, и этой самой "асинхронности" в частности
Т.е. async await это паттерн - определённая реализация идеи средствами компилятора и вспомогательных классов - но он САМ требует использования определённого паттерна при написании кода


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Асинхронность
S-type

Сообщений: 2969
Дата регистрации: 24.04.2004
Вот, нарисовал схему вызовов.

[attachment 27760 sss1.png]

Правильно?
Ratings: 0 negative/0 positive
Re: Асинхронность
S-type

Сообщений: 2969
Дата регистрации: 24.04.2004
Igor Korolyov
Т.е. async await это паттерн - определённая реализация идеи средствами компилятора и вспомогательных классов -
Это понятно, это речь о Task-based Asynchronous Pattern (TAP) msdn.microsoft.com

Igor Korolyov
но он САМ требует использования определённого паттерна при написании кода
А это не понятно - о каком ещё паттерне идёт речь?
Ratings: 0 negative/0 positive
Re: Асинхронность
Igor Korolyov
Автор

Сообщений: 34580
Дата регистрации: 28.05.2002
S-type
Igor Korolyov
но он САМ требует использования определённого паттерна при написании кода
А это не понятно - о каком ещё паттерне идёт речь?
О том как правильно использовать эти async/await. А там куча разных нюансов есть...


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Асинхронность
S-type

Сообщений: 2969
Дата регистрации: 24.04.2004
А как лучше делать:

static async Task<string> myFuncAsync()
{
WebClient client = new WebClient();
return await client.DownloadStringTaskAsync(@"https://ya.ru");
}

или

static async Task<string> myFuncAsync()
{
return await new WebClient().DownloadStringTaskAsync(@"https://ya.ru");
}

Мне кажется, что второй вариант лучше в том плане, что переменная client не попадает в "замыкание". Или, всё равно, как делать?
Ratings: 0 negative/0 positive
Re: Асинхронность
Igor Korolyov
Автор

Сообщений: 34580
Дата регистрации: 28.05.2002
Мне кажется что без разницы. Всё одно ссылку на WebClient будет держать асинхронная реализация операции, так что от "лишней" ссылки в классе state-машины вреда я не вижу...
А в общем случае между созданием WebClient и запуском асинхронной операции может быть ещё несколько команд настройки - "лепить" всё это в одну строку как-то не очень красиво будет... Кстати, и ПОСЛЕ запуска DownloadStringTaskAsync вполне может быть ещё какой-то код, например что-то делающий со скачанной строкой и возвращающий уже результат после обработки...


------------------
WBR, Igor
Ratings: 0 negative/0 positive


Извините, только зарегистрированные пользователи могут оставлять сообщения в этом форуме.

On-line: 17 (Гостей: 17)

© 2000-2024 Fox Club 
Яндекс.Метрика