« first day (551 days earlier)      last day (2766 days later) » 

10:00 PM
@avp никуда, он хранится в поле класса SpecialClass
 
avp
@VladD не понимаю. Что в регистре IC?
 
@avp IC?
 
avp
@VladD IC (PC) -- счетчик команд железного потока
 
а, понял
ну, смотрите.
код на самом деле выглядит так:
this.i1 = google.Length;
this.wc2 = new WebClient();
this.task2 = wc2.DownloadStringAsync("http://stackoverflow.com");
когда старый task1 завершился, он выполнил callback в каком-то потоке
вот в этом потоке и произошло выполнение вот этого куска
после чего await подписался на окончание следующего таска (task2), и выполнение куска завершилось
наш таск (resulttask) при этом ещё не завершён
есть?
 
avp
@VladD но чем отличаются первый await, который вернул незавершенный task в caller и этот второй? Не понимаю
 
10:05 PM
@avp первый await вернул не task1, а resulttask.
и в этой схеме, да, первый await особенный
потому что до первого await функция F() ведёт себя как нормальная
а при первом await она завершается, возвращая незавершённый таск
 
avp
@VladD тут точно голову сломать можно, а не задачку запрограммировать
 
@VladD эдакий СompletableFuture с неявными коллбэками получается
 
@avp тут легко привыкнуть, правда
@Nofate он самый
 
а в стекрейсах это все как смотрится? .нет как-то красиво это показывает?
 
@Nofate да, он сводит их вместе. он даже исключения маршаллирует
 
avp
10:08 PM
@VladD а старый блок AIO с его состояниями и возможностью ждать завершения чем хуже?
 
@avp А что такое AIO?
я не в курсе
ну и здесь нет блокирующего ожидания завершения
 
avp
@VladD Асинхронный ввод-вывод (еще с 60-х)
 
@avp А!
 
если в F() ниже await будет исключение брошено, в стектрейсе мы увидим F() или синтетическую функцию?
 
@avp здесь есть await, который прекращает выполнение функции, и возодновляет его при приходе колбека
@Nofate F(). потому что синтетическая функция так и называется
 
avp
10:10 PM
@VladD вот это и сбивает с толку
 
@avp ага, немного необычно
 
а это не удорожает и без того дорогой механизм исключений?
 
avp
@VladD это я понимаю. Я не понимаю, как логику caller программировать
 
@Nofate он не такой дорогой, по сравнению с например типичным для этой техники ожиданием загрузки из инета или чтением файла
@avp сейчас дойдём до этого
едем дальше
итак, у нас завершился task2
выполняется код
 
avp
@VladD select/poll/epoll чем не устраивает? Мне кажется, что этот высокий уровень весьма непонятен.
 
10:12 PM
this.i2 = stackoverflow.Length;
return this.i1 + this.i2;
 
@avp удобно писать асинхронный код, который выглядит как синхронный
 
@avp без async/await сложнее программировать сложную логику
 
без этого будет лапша из коллбэков
 
@avp итак, что происходит в точке return?
 
avp
@VladD Да
 
10:14 PM
в наш returntask записывается результат
returntask переводится в состояние «завершён»
теперь смотрится, какие у нас к нему навешены колбеки
и эти колбеки выполняются
все подряд, один за другим
таким образом!
если caller написал await F()
то в этот момент caller тоже нигде не выполняется, и единственная его связь с миром — колбек, которым он подписан на окончание нашего таска
 
avp
@VladD тут понятно, все также, как разбирали, callback у caller
 
@avp да!
то есть в этот момент он по цепочке вниз возобновляет работу
и у него есть наш результат
 
avp
@VladD а если не написал await?
 
@avp например, caller может записать Task<int> в переменную
тогда он продолжит выполнение параллельно нашему бегущему таску
когда-нибудь он на этот таск сделает await
и в этот момент произойдёт всё то же
или никогда не сделает
тогда таск просто выполнится в фоне, и никто о его результате не узнает
 
@VladD, это все поддержание цепочки коллбэков. но где-то надо же в какой-то трэдпул сунуть задачу на выполнение и где-то залочиться на i/o? кто этим управляет?
 
10:20 PM
@Nofate смотрите
есть IO-таски
к ним завершение приходит на IO Completion thread
там, где и все колбеки асинхронного IO
 
ШЩ это хорошо.
 
:)
есть таски, завершающиеся по таймеру, они стартуют цепочку в таймеровом потоке
 
ммм. ок. этот ио комплишен тред - в каком-то системном тредпуле?
 
В IO Completion, видимо, работает select или аналог, сразу на все источники. Возвращает первый проснувшийся.
 
10:22 PM
ну или если какой-то код хочет выполнить что-то тяжёлое, он может отправить в отдельный поток
@Nofate ага
 
и еще. в случае с футурами в джаве в коллбэке выполнять тяжелый таск считается не очень хорошо - где-то грустит тредпул, у которого мы занимаем поток.
 
@Nofate Да, для этого есть Task.Run
 
или коллбэки в цепочке будут каждый раз в разных потоках приходить?
 
если нужно тяжёлый код отправить в отдельный поток, нужно использовать Task.Run
@Nofate Тут как бы кооперативная многозадачность. Если мой код внутри async-функции лёгкий, я могу выполнить его по месту, где бог на душу пошлёт.
А если он тяжёлый, я должен не выполнять его прямо где попало, а написать await Task.Run(() => { тяжёлый код }).
при этом тяжёлый код выгрузится в отдельный поток
 
@VladD просто я привык, что все тредпулы надо явно конфигурировать/создавать
а тут все сабмитится в какое-то волшебное место и там же колбэчится )
 
10:26 PM
@Nofate Ну, Task.Run как раз предназначен для таких ситуаций.
@Nofate Не, волшебного места нет, и думать об этом не надо, если код лёгкий
 
avp
@VladD, не верю я что-то в надежность таких программ (не маленьких демо, и даже не ОС (там все похоже, но ОС это по сути библиотека, она своей логикой не обладает) а серьезных прикладных программ со сложной логикой)
 
Ага, там вместо обобщённого IO используются overlapped-структуры для каждого отдельного случая IO, и подобие select происходит неявно, когда запускается несколько overlapped-операций одновременно, и куда отправляется результат, определяет соответствующая структура. Прикольно.
 
@avp Два года полёт нормальный. Сложная бизнес-логика прекрасно работает.
 
avp
@VladD хорошо, если так. А уровень программеров? Как у Вас?
 
@D-side Ага, именно так. Смысл overlapped IO в этом.
@avp не, наоборот, смысл async/await в том, чтобы сделать асинхронное программирование лёгким
нужно просто действовать по простому правилу
писать как обычно, а если результат есть таск, сделать await
ну и можно запустить несколько тасков параллельно
например, так:
 
10:29 PM
@VladD спасибо за подробный обзор. у меня все прояснилось )
 
var t1 = F();
var t2 = G();
await t1;
await t2;
@Nofate пожалуйста!
 
кстати про несколько тасков
 
@Nofate да?
 
популярная задача при работе с футурами и монадами вообще - преобразовать список футур в футуру на список
 
и тут nofate вкрадчиво спросил: есть два таска, в одном процедуры точены, в другом объекты мочены
2
ну я почти угадал
 
avp
10:31 PM
@VladD стоп. А разве второй await будет не в callback первого? Где тут параллельность? Или я уже опять запутался?
 
@Nofate await Task.WhenAll(tasks)
 
@Vlad нене. ручками )
 
Task.WhenAll(tasks) даёт таск на список
@Nofate да боже мой :)
сейчас напишу
 
@avp да, но G запущен будет до него, т. к. "перерыв на коллбэк" будет на первом await, как я понимаю. Поэтому параллельность таки будет.
 
ну ок. свежий пример из жизни. есть список тасков. нужен таск на список. если таски не уложились в 100 мс - должны вернуть null при этом.
 
10:33 PM
async Task<T[]> WhenAll<T>(IEnumerable<Task<T>> tasks)
{
    List<T> result = new List<T>();
    foreach (var t in tasks)
        result.Add(await t);
    return result.ToArray();
}
@Nofate Тоже не проблема, сейчас
 
avp
@D-side не понимаю. Первый возвращается в caller, потом работает callback, в нем запускается второй
 
то что await - expression очень удобно, однако
 
avp
@D-side ОК, понял.
 
@avp F() возвращается в caller, в t1 выполняющийся таск, G() возвращается в caller, в t2 выполняющийся таск. await t1 дожидается, пока закончится t1 и получает его значение. Но t2 при этом тоже запущен и работает, пусть на него пока что всем и плевать.
 
async Task<T> AddTimeout(Task<T> t, TimeSpan ts)
{
    var waitTask = Task.Delay(ts);
    var winner = await Task.WhenAny(t, waitTask);
    if (winner != t)
        throw new TimeoutException();
    return await t;
}
@avp await добавляет колбек своему таску. когда t1 завершится, пойдёит дальше выполняться хвост. при этом t2 бежит параллельно
 
avp
10:37 PM
@D-side да, понял. Не понимаю, как уследить за всеми task в caller, но это уже непоправимо -)
 
@VladD круто
 
@avp ну у вас на каждый есть переменная. удобно просто писать что-то типа int x = await F() + await G(), и не думать о тасках
 
хотя все равно не понятно в каком потоке выполнится Task.Delay(ts);
 
@Nofate их очень удобно комбинировать
@Nofate ни в каком (звучит очень по-Пелевински)
 
Похоже, что org.clojure/core.async работает идентично, но в немного других абстракциях :)
 
10:38 PM
он подписываается на таймер, и в таймеровском потоке приходит колбек
 
@VladD таймеровский поток общий на виртуальную машину?
 
@Nofate думаю, да
 
Ну, т. е. он "выполняется" в таймерском потоке, хотя фактически он ничего не делает, время процессорное не занимает, лишь ждёт, пока ему сообщат о срабатывании таймера.
 
avp
@VladD но тут я же вызываю F() и G() последовательно? И первый await F() делает return в верхний caller?
 
хотя зависит от таймера, некоторые таймеры бросают колбек в пул потоков
 
10:40 PM
для сравнения на последней java задача решается не достаточно симпатично, хотя и лучше чем на 7ке
 
@avp да
 
public static <T> CompletableFuture<List<T>> waitForAll(
List<CompletableFuture<T>> rawFutures,
int timeout,
ScheduledExecutorService scheduler) {

scheduler.schedule(() -> {
for (CompletableFuture<T> rawFuture : rawFutures) {
if (!rawFuture.isDone()) {
rawFuture.complete(null);
}
}
}, timeout, MILLISECONDS);

return CompletableFuture.allOf(rawFutures.toArray(new CompletableFuture[rawFutures.size()]))
.thenApply(_void -> rawFutures.stream().map(f -> f.getNow(null)).collect(Collectors.toList()));
 
@avp этот код аналогичен коду с временными переменными, их компилятор подставит сам, если надо
 
{ var tmp1 = await F(); var tmp2 = await G(); tmp1 + tmp2 }
 
10:42 PM
было познавательно, всем до завтра!
 
@Nofate До завтра!
 
@Nofate пока
 
@avp если хочется параллельно, надо самому вручную:
 
ru.stackoverflow.com/review/suggested-edits/111255 -- ну вот опять кто-то забэктикал вопрос ._.
 
avp
@VladD а каким образом throw Timeout прерывает t? Или не прерывает, просто результат t будет потерян?
 
10:44 PM
var t1 = F();
var t2 = F();
await t1 + await t2
@avp просто будет потерян
в принципе, у таска может быть ниточка, за которую можно потянуть и отменить его
называется CancellationToken
таск должен, правда, сам его проверять и самоотменяться, если что
в этом случае мы можем дёрнуть за токен и попросить таск самоубиться
а если у нас токена нет, то таск просто молча добежит, и результат будет никому не нужен
 
avp
@VladD вообще интересная картина. Т.е. main thread как бы быстро умирает, а все работает в самоподдерживающейся цепочке callback-ов. Как среда исполнения разбирается со стеками?
 
@avp Main thread должен при этом дождаться конца работы. Потому что если main thread заканчивается, это убивает всю программу
то есть практически это означает, что main thread запускает стартовую задачу (task), и занимается ожиданием её конца
 
avp
@VladD, если вернуться к


1 int result = 0;
2 while (result >= 0)
3 {
4 result = await ComputeNext(result);
5 if (result % 2 == 0)
6 result += await ComputeCorrection(result);
7 if (result == 42)
8 return result;
9 }
10 return -result;

то в цикле второе и т.д. выполнение строки 4) проходит в рамкахх коллбэка строки 6)?
 
в следующей версии C# даже сделают async Main
@avp Да!
 
avp
@VladD это понятно (я имел в виду логически умирает, перестает что-то полезное делать). Аналогично main loop в UI (или др. event driven системе)
 
10:53 PM
второе выполнение строки 4 — в рамках выполнения первого колбека строки 6.
@avp именно так
третье выполнение строки 4 — в рамках выполнения второго колбека строки 6.
и т. д.
то есть колбеки строки 6 конечно одинаковые
 
avp
@VladD огромное спасибо! Пошел курить
 
так что в рамках второго выполнения колбека строки 6.
@avp Пожалуйста! Был рад поделиться.
 
avp
@VladD в живом общении лучше доходит (можно переспросить)
 
@avp Да! Ну и обычно в документации объясняют не с позиции специалиста, а с позиции юзера.
 
avp
@VladD еще раз, огромное спасибо! (и спокойной ночи -))
 
11:03 PM
@avp Очень рад был оказаться полезным! Спокойной ночи!
 

« first day (551 days earlier)      last day (2766 days later) »