Task.Run(...) действительно всегда отправляет делегат по маршруту TaskScheduler.Default → ThreadPoolTaskScheduler → ThreadPool. И тем самым мы сразу и надёжно отвязываемся от SynchronizationContext.Current.
await DoAsync().ConfigureAwait(false) будет иметь эффект только в случае фактической асинхронности. Например:
Приостанавливает (suspend) означает, что метод (сопрограмма) подписывает себя на асинхронное продолжение (continuation) и выходит (делает return), освобождая поток.
Синхронный код ждёт, занимая/блокируя поток через busy wait или примитивы синхронизации ОС.
Скорее всего, вы имеете в виду отсутствие параллелизма в пределах метода (типа WhenAll или ForAsync). От этого ни сам метод, ни дочерние вызовы не перестают быть асинхронными. Хронологически, между Read и Write может успеть выполниться часть другого асинхронного метода. Другими словами, может случиться кооперативная многозадачность на уровне приложения (как раз то, зачем придумали async/await).
гораздо эффективнее и проще вызвать аналогичную синхронную функцию
Я бы не рискнул давать такой совет хотя бы потому, что мы не знаем конкретного call-стека. В контексте статьи спорить не о чем - здесь код лишь иллюстрирует преобразование компилятора.
Цель - "понять, как работает async/await" (это написано в заголовке статьи).
Вы пишете про самый первый пример: "зачем-то асинхронные функции вызываются синхронно". Но ведь это не так. В нём асинхронно вызываются асинхронные функции - await File.XxxAllTextAsync.
Магия async/await (то есть преобразование кода, проводимое компилятором) никогда не создаёт потоки сама. Она только комбинирует результаты других вызовов через GetAwaiter и AsyncXXXMethodBuilder. При этом даже тип Task не обязателен - см. Generalized async return types and ValueTask.
runit, используемый в качестве менеджера процессов, очень неудобен в использовании, особенно когда что-то идёт не так. Supervisord куда более приятен и к тому же сам умеет делать «reape unknown pid», так что «изобретения» в виде my_init не нужны.
Ни ssh, ни nsenter теперь не являются необходимыми. Для входа в контейнер используется команда docker exec -ti CONTAINER_ID bash. Для более удобной работы в консоли внутри контейнера (запуск mc и тп.) рекомендую добавить в Dockerfile:
ENV LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 TERM=xterm
RUN locale-gen $LC_ALL
Самое опасное: cron и logrotate пускаются со стандартными конфигами, не рассчитанными на работу в контейнерах. Пример: установили Apache, получили файл /etc/logrotate.d/apache2, внутри которого используется /etc/init.d, значит runit потеряет контроль над процессом после первой ротации логов.
1 3 ms 2 ms 2 ms
2 27 ms 26 ms 26 ms
3 33 ms 28 ms 28 ms
4 30 ms 31 ms 27 ms
5 33 ms 31 ms 32 ms 77.51.254.234
6 90 ms 74 ms 74 ms ae-5.626.m7-cr1-b.msk.ip.rostelecom.ru [188.254.36.101]
7 133 ms 144 ms 136 ms 95.167.91.155
8 174 ms 162 ms 166 ms 61.213.146.253
9 185 ms 185 ms 162 ms ae-0.r23.tokyjp01.jp.bb.gin.ntt.net [129.250.4.106]
10 236 ms 232 ms 231 ms as-6.r21.sngpsi02.sg.bb.gin.ntt.net [129.250.5.157]
11 233 ms 225 ms 241 ms ae-4.r20.sngpsi02.sg.bb.gin.ntt.net [129.250.6.100]
12 227 ms 226 ms 228 ms 116.51.27.150
13 299 ms 226 ms 225 ms 103.253.144.242
14 246 ms 254 ms 246 ms 128.199.244.8
Ping statistics for 128.199.244.8:
Minimum = 247ms, Maximum = 251ms, Average = 249ms
Автор хотел более доходчиво донести «физику явления». А формулы и их выводы можно в книжках посмотреть. Мне, например, очень понравилась часть статьи, где объясняется, почему применение окон приводит к растеканию спектра.
Раздел с PDF-ами доступен, но не уверен, что в них есть ответ на мой вопрос. Интересует документация в духе такой или такой.
Там легко понять, что для Chrome Store нужно загрузить zip c иконкой и манифестом, а для Firefox Marketplace просто указать ссылку на .webapp-файл. Что делать для Tizen? В панели разработчика просят загрузить абстрактный Binary File.
Есть ли хотя бы минимальный туториал о том, как опубликовать hosted-приложение в store? Гугление по «tizen hosted applications» не дает практически ничего.
Есть неточности. Вот как правильно:
Task.Run(...)
действительно всегда отправляет делегат по маршрутуTaskScheduler.Default
→ThreadPoolTaskScheduler
→ThreadPool
. И тем самым мы сразу и надёжно отвязываемся отSynchronizationContext.Current
.await DoAsync().ConfigureAwait(false)
будет иметь эффект только в случае фактической асинхронности. Например:Поэтому
ConfigureAwait
приходится дописывать каждый раз, к каждомуawait
.И с терминологией важно не путаться:
SynchronizationContext
- контекст синхронизации - планировщик, в который отправляются await continuations.ExecutionContext
- контекст выполнения - место хранения async locals.Приостанавливает (suspend) означает, что метод (сопрограмма) подписывает себя на асинхронное продолжение (continuation) и выходит (делает return), освобождая поток.
Синхронный код ждёт, занимая/блокируя поток через busy wait или примитивы синхронизации ОС.
Скорее всего, вы имеете в виду отсутствие параллелизма в пределах метода (типа
WhenAll
илиForAsync
). От этого ни сам метод, ни дочерние вызовы не перестают быть асинхронными. Хронологически, междуRead
иWrite
может успеть выполниться часть другого асинхронного метода. Другими словами, может случиться кооперативная многозадачность на уровне приложения (как раз то, зачем придумалиasync/await
).Я бы не рискнул давать такой совет хотя бы потому, что мы не знаем конкретного call-стека. В контексте статьи спорить не о чем - здесь код лишь иллюстрирует преобразование компилятора.
Цель - "понять, как работает async/await" (это написано в заголовке статьи).
Вы пишете про самый первый пример: "зачем-то асинхронные функции вызываются синхронно". Но ведь это не так. В нём асинхронно вызываются асинхронные функции -
await File.XxxAllTextAsync
.И правда, моя формулировка получилась слишком общей. Добавил уточнение:
Магия
async/await
(то есть преобразование кода, проводимое компилятором) никогда не создаёт потоки сама. Она только комбинирует результаты других вызовов черезGetAwaiter
иAsyncXXXMethodBuilder
. При этом даже типTask
не обязателен - см. Generalized async return types and ValueTask.echo
, т.к. обходятся только запущенные контейнеры.docker exec -ti CONTAINER_ID bash
. Для более удобной работы в консоли внутри контейнера (запуск mc и тп.) рекомендую добавить в Dockerfile:/etc/logrotate.d/apache2
, внутри которого используется/etc/init.d
, значит runit потеряет контроль над процессом после первой ротации логов.После установки создайте файл /etc/dnsmasq.d/docker с контентом
Теперь после добавления нового контейнера или пересоздания/перезапуска существующего надо будет выполнить примерно такой скрипт:
Минусы:
Плюсы:
Идея взята отсюда.
Сигнал ограничен во времени => спектр растекается (см. Benedicks's theorem). Спектр растекается => невозможно применить теорему Котельникова.
Там легко понять, что для Chrome Store нужно загрузить zip c иконкой и манифестом, а для Firefox Marketplace просто указать ссылку на .webapp-файл. Что делать для Tizen? В панели разработчика просят загрузить абстрактный Binary File.
Уточню: интересуют именно hosted web-приложения.
Возможно, WinJS задумывался как технология для быстрого наполнения Windows Store всякой мутью.
Правда после первого серьезного опыта (попытки написать не hello world) начинаешь четко понимать: да, так жить нельзя. Не минусуйте его! )