Здравствуйте, Serginio1, Вы писали:
S>Это как может быть поток без окна...
+100500
Да именно!
Это working-thread — который делает некоторую работу, не относящуюся непосредственно к GUI.
Здравствуйте, Danchik, Вы писали:
D>>>Вот хотелось бы знать что так возмущает. Если задача упирается в сеть или диск — async/await именно то что доктор прописал. _>>Ну вот простейшая классическая задача: скопировать файл (читаем его в буфер в памяти и параллельно записываем на диск в новое место). Эта задача собственно вот только в диск и упирается. И ты реально считаешь, что для её реализации будут эффективнее сопрограммы с асинхронным вводом-выводом? ))) D>Не будем вдаваться в крайности. То что требует дикой скорсти я запущу в одном потоке или распаралелю как надо без асинков.
Вот, ты понимаешь! Только ещё замечу, что во многих задачах это не крайность, а норма. )
_>>Вот, здесь уже всё сказано корректно (ну разве что apache на потоках держал всё же побольше 2-3 пользователей). Только вот ты как-то забываешь, что основной сценарий из мира бэкенда, является вырожденным практически во всех остальных областях. D>Не используй если не надо, хорошо что оно есть. У меня бакенды основное.
Так я и сказал с самого начала, что это полезный инструмент для бэкенда. Правда не всего, а только нагруженного. А то у меня тут главная задача по сути тоже является бэкендом классической архитектуры (ну разве что клиент в браузере в виде wasm, а не js), но с одним принципиальным нюансом — более одного одновременного клиента у него не может быть в принципе.
Здравствуйте, Mystic Artifact, Вы писали:
_>>Ну вот простейшая классическая задача: скопировать файл (читаем его в буфер в памяти и параллельно записываем на диск в новое место). Эта задача собственно вот только в диск и упирается. И ты реально считаешь, что для её реализации будут эффективнее сопрограммы с асинхронным вводом-выводом? ))) MA> Она не простейшая. В FAR был почивший? плагин делающий это хотя бы эффективно. Нужно один/два больших буфера (очень больших, типа 16Мб+) — в него асинхронно читаешь максимально большими блоками (буфер должен уметь в много файлов) — ну параллельно пишешь. Тебе не нужно 2 потока для чтнения/записи и их данных — ты можешь просто дирижировать из одного потока, тем самым не израсходовав ресурсы (стэк, прочие ресурсы на поток). Стратегии копирования с одним HDD — одни, а с несколькими — другие.
Если мы говорим о нескольких потоках (а не о тысячах), то это нельзя считать тратой ресурсов. ) А приносит это очень существенную прибыль: упрощение архитектуры + увеличение надёжности (GUI точно не станет подвисать, как это возможно в случае async при классическом подходе).
_>>Или же просто запустить эту операцию в отдельном потоке. ))) Что будет намного проще и эффективнее. MA> Вернемся к тому, что нужен будет "арбитр" связующий задание и UI и трэкающий события. Вспоминаем классику MFC и радуемся беспорядочным посылкам непонятно чего. Ничего проще тут нет.
Не очень понял причём тут MFC. Но если мы говорим о UI потоке, то он и так априори сидит на цикле сообщений, так что добавление ему обработки ещё одного, нового, не даст никаких усложнений. Ну а исполняющему задачу потоку не надо вообще ничего "трекать", т.к. он будет работать с синхронным API.
MA>Ну про преимущества пула vs на каждый чих создавать потоки можно долго спорить (я бы лично создавал чаще, чем держал 10 бесполезных потоков). Но сейчас то точно все тюнится. В чем проблема. Более того таскм есть LongRunning. Предпочитаешь Thread vs TPL — твое право. И то и то имеет место быть в практике.
Пул потоков (обычно с числом потоков, равным числу логических ядер) полезен в основном для тех задач, которые полностью загружают процессор. Это как раз случаи нагруженных сервисов (тогда в каждом таком потоке тысячи асинхронных задач крутятся) и случае различных вычислений (тут просто распараллеливают набор данных по числу ядер).
А вот для случая привнесения "пользовательской асинхронности" (т.е. когда грубо говоря задача на закачку файла не блокирует весь UI до её исполнения) как раз правильнее использовать классический запуск фонового потока.
Здравствуйте, ononim, Вы писали:
AG>>Ранее, ЕМНИП во времена Windows 3.11, понятие "асинхронность" не реализовывалось через multi-threading. AG>>Тогда были специальные "асинхронные" реализации. Которые, ради совместимости, существуют и по сей день (но реально уже давно не используются). AG>>Теперь (последние лет 20-ть): асинхронность реализуется через multi-threading. O>Все с точностью до наоборот. Внутри винды весь ИО асинзронен, есть асинхронное АПИ — оно более "нативно", есть синхронное — оно внутри реализовано через асинхронное + ожидание его завершения.
Какие забавные фантазии. С большим интересом послушаю о том, как именно реализован прямой асинхронный вызов из драйвера сетевой карты (для примера) в спящий iocp поток нашего приложения.
_ _>А вот для случая привнесения "пользовательской асинхронности" (т.е. когда грубо говоря задача на закачку файла не блокирует весь UI до её исполнения) как раз правильнее использовать классический запуск фонового потока.
И чем это лучше?
Вместо awaite File.ReadAllTextAsync нужно создавать бэкграунд поток, передавать в него метод в котором вызывать в итоге File.ReadAllText
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Думать придется об общей нагрузке. О затратах на переключение контекста думать нет смысла — любые вычисления их многократно превосходят.
Вот в древнем apache как раз именно так и считали, что нет смысла об этом думать. Только вот после выхода nginx'а (автор которого придерживался прямо противоположной точки зрения), почему то все серьёзно нагруженные сайты резко перебежали на него. )))
Мне недосуг разбираться в деталях задачи, тем более, что полное описание по тамошней ссылке недоступно, но с ходу непонятно, каковы условия завершения этого кода. Судя по всему, сутью его работы является конкуренция за ресурс (место встречи). То есть, при реализации через потоки ОС код практически не выполняет полезной работы, а только переключает контексты с максимально возможной скоростью. Это и приводит к тому, что накладные расходы получаются огромными.
Если же код выполняет реально полезную работу, и переключает контекст только в реально необходимых случаях, то разница между переключением сто или тысячу раз в секунду будет совершенно незаметна.
N>Когда-то давно у Рихтера читал, что это до 2 тысяч тактов, а при переходе ещё по разным ядрам может быть и того хуже. Разве нет?
Верно. Но тысяча раз в секунду по две тысячи тактов — это два миллиона тактов, что составляет 0.1% на частоте 2 ГГц. А подавляющее большинство современного высокоуровневого кода достаточно легко оптимизируется если не на порядки, то хотя бы в разы. Что, собственно, и показал remark своей работой.
Вот у меня сейчас винда, в которой не происходит никакой полезной работы, и общая загрузка процессора около 3%, делает около 50000 переключений контекста в секунду. При мало-мальски активной работе оно подскакивает до 120000.
Здравствуйте, alex_public, Вы писали:
_>Вот в древнем apache как раз именно так и считали, что нет смысла об этом думать. Только вот после выхода nginx'а (автор которого придерживался прямо противоположной точки зрения), почему то все серьёзно нагруженные сайты резко перебежали на него. )))
Я не смотрел код Apache, но, судя по функциональности, структуре конфигурационных файлов, способам управления и т.п., там уже давно сам черт ногу сломит. Вряд ли проблемы с его производительностью непосредственно связаны с затратами на переключение контекста.
MA> UPD: Это галопом по галактике, очень грубо. Я едва понял что ты там написал.
Я писал про BeginWrite и BeginRead. Которые не используют threadpool, если не передавать callback. Можно не задействовать threadpool, если все, что нужно — узнать результат операции записи. А пока оно там пишется, можно прочесть еще данных из источника, или посчитать что-то несложное.
А с нынешним async-await как мы управляем созданием потоков? Никак. Что там с потоком(из threadpoll) происходит, пока идет запись в сеть или на диск — непонятно. Висит впустую, ест ресурсы почем зря.
В общем, я ожидал большего от асинков. А так — те же потоки, только управляемые автоматически. Ну, ок.
O>>Все с точностью до наоборот. Внутри винды весь ИО асинзронен, есть асинхронное АПИ — оно более "нативно", есть синхронное — оно внутри реализовано через асинхронное + ожидание его завершения. _>Какие забавные фантазии. С большим интересом послушаю о том, как именно реализован прямой асинхронный вызов из драйвера сетевой карты (для примера) в спящий iocp поток нашего приложения.
Работа с сетью через IOCP это и есть едва ли не сферический пример юзания асинхронного АПИ, что сказать-то хотели?
Как много веселых ребят, и все делают велосипед...
Здравствуйте, Mystic Artifact, Вы писали:
MA> Я отстал от жизни. А кто сейчас может в 4 потока на ядро? И что это значит? Какие ресурсы ядра при этом разделяются?
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>В контексте этого обсуждения большинство потоков бОльшую часть времени ни хрена не делает.
Если ограничиться UI-ем, то да.
ЕМ>Думать придется об общей нагрузке. О затратах на переключение контекста думать нет смысла — любые вычисления их многократно превосходят.
Превосходить-то они превосходят, только вот если их еще и пытаться излишне параллелить, то переключения контекста тоже начнут отжирать приличную часть времени.
Здравствуйте, Lexey, Вы писали:
ЕМ>>О затратах на переключение контекста думать нет смысла — любые вычисления их многократно превосходят.
L>Превосходить-то они превосходят, только вот если их еще и пытаться излишне параллелить, то переключения контекста тоже начнут отжирать приличную часть времени.
Чтобы переключения контекста стали отжирать приличную часть времени, их нужно сделать тысячи раз в секунду. "Излишне параллеля" обычные вычисления, это можно получить только созданием сотен потоков, а это как раз тот критерий, который я приводил в самом начале ветки.
Наиболее реальный (не тестовый) сценарий, в котором уже пора присматриваться — часто (сотни-тысячи в секунду) приходящие события, для обработки которых используется несколько потоков.
Здравствуйте, Kolesiki, Вы писали:
K>Мне опыт говорит, что это всё переоценённое фуфло. Но если у вас есть прям идеальные случаи под async, не поленитесь, черкните ваш случай! K>Спасибо!
K>UPD K>Как и ожидал, несмотря на тонны материалов по thread и async, некоторые до сих пор думают, что это одно и то же.
Вне контекста C# с его async-ами, представим, что у нас есть гуи приложение с лентой новостей, a-la лента новостей facebook, twitter и тд. Мы загружаем, скажем, первые 100 элементов и пользователь может скроллить вниз, пока не дойдет до конца и мы должны будем загрузить следующие 100 элементов и тд.
Тут нужно загружать сами элементы, плюс всякие media внутри них, скажем новость может содержать ссылку на сторонний сайт и мы должны загрузить страничку, сделать превью и вставить его в новость, либо новость может содержать видео/картинку, тогда мы должны сделать еще один запрос и загрузить превьюшку. Хорошо бы еще делать read-ahead, вместо того, чтобы ждать когда пользователь доскроллит до конца списка, можно начать загружать следующие 100 элементов, когда он будет где-нибудь в районе 70-го элемента. Это же касается медиа. Не стоит ждать когда пользователь доскроллит до новости, лучше начать делать это немного заранее.
Я надеюсь всем понятно, что использование модальных окон здесь неуместно. Самый оптимальный вариант — делать все асинхронно. Можно запускать отдельный поток на каждую операцию. Тут есть следующие проблемы:
— Синхронизация, нужно как-то передать данные в гуи поток, нужно также запустить вовремя поток, убедиться что он стартовал и начал асинхронную операцию, нужно его остановить, если данные, которые он загружает больше не потребуются и тд.
— Управление ресурсами, нужно обеспечить правильное освобождение ресурсов потоками, сокеты должны быть закрыты, сам поток остановлен и тд. Это сложно обеспечить, когда у тебя есть такие I/O потоки, которые, скажем могут висеть по 5-10 минут на recv, потому что пользователь подключен к интернету через медленный 3G модем и получает данные по крупицам, при этом скроллит ленту новостей быстро и у тебя эти потоки создаются тысячами и висят в памяти.
— Память. Каждый поток это примерно один мегабайт памяти + кусок адресного пространства + при старте стек потока должен быть инициализирован + должны быть созданы структуры ядра и тд. Запуск потока это довольно дорого, поэтому для коротких но многочисленных операций ввода-вывода они плохо подходят.
Можно попытаться решить эти проблемы делая все в пулле потоков. Вместо того, чтобы запускать поток каждый раз, мы можем создать пулл потоков, который был бы интегрирован с I/O, чтобы мы могли ставить задачи на скачивание медиа или превью веб страниц в очередь и пулл бы их выгребал и запускал наши коллбэки, которые бы это все преобразовывали в нужный вид и отдавали бы в гуи. Это решает проблему управления ресурсами и проблему потребления ресурсов, остается только синхронизация с гуи потоком, которая становится сложнее и prone to deadlocks, потому что мы используем обратные вызовы. Помимо этого, у нас появляется новая проблема — код становится сложнее!
Задача загрузки превью для страницы состоит из нескольких частей, нужно загрузить страницу по http, проанализировать ее и найти на ней <img> теги, выбрать тот из них, который будет использоваться в превью и загрузить картинку из него еще одним http запросом. Т.е. по сути, внутри нашего колбэка для обработки первого http запроса должен шедулиться еще один http запрос, при этом в него должен передаваться контекст выполнения первого колбэка. В коде это выглядит как два разных, скажем, метода. Чтобы проследить логику выполнения, нужно найти в коде место где шедулится первый http запрос, определить, какой коллбэк будет вызываться, когда мы получим данные, потом посмотреть в этот коллбэк и найти там место, в котором шедулится следующий колбэк и посмотреть его код. Нужно также учесть время жизни объектов, которые создаются в первом колбэке и передаются во второй, а также обработать ошибки, вне зависимости от того, в каком из колбэков они происходят.
Ну и два уровня вложенности коллбэков, это далеко не предел, скажем мы можем во втором коллбэке отпрвлять какие-либо метрики на сервер или кешировать полученный превью в файле и тд.
To get our ducks in a row, мы можем захотеть как-нибудь упросить это. Желательно сделать так, чтобы этот код выглядел также как его синхронный однопоточный вариант. Преуспев в этом, мы получим свою собственную, костыльную реализацию async/await.
N>А с нынешним async-await как мы управляем созданием потоков? Никак. Что там с потоком(из threadpoll) происходит, пока идет запись в сеть или на диск — непонятно. Висит впустую, ест ресурсы почем зря.
Здравствуйте, namespace, Вы писали:
N>А с нынешним async-await как мы управляем созданием потоков? Никак.
async метод только разворачивает машину состояний. Он может и вовсе синхронно вернуть результат. Запуск задачи на пуле потоков как и раньше, происходит явно, через все возможные апи. Вместо запуска какой-то задачи, в асинхронном методе вполне может лежать синхронный IPC с возвратом Task с результатом, который появится потом.
await тоже не прибит гвоздями к стенке.
Здравствуйте, Mystic Artifact, Вы писали:
MA>Здравствуйте, namespace, Вы писали:
N>>А с нынешним async-await как мы управляем созданием потоков? Никак. MA> async метод только разворачивает машину состояний. Он может и вовсе синхронно вернуть результат. Запуск задачи на пуле потоков как и раньше, происходит явно, через все возможные апи. Вместо запуска какой-то задачи, в асинхронном методе вполне может лежать синхронный IPC с возвратом Task с результатом, который появится потом. MA> await тоже не прибит гвоздями к стенке.
ValueTask https://habr.com/ru/post/458828/
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Mystic Artifact, Вы писали:
MA>Здравствуйте, Serginio1, Вы писали:
S>> ValueTask S>>https://habr.com/ru/post/458828/ MA> Что я должен нового узнать на этом недосайте?
Вместо запуска какой-то задачи, в асинхронном методе вполне может лежать синхронный IPC с возвратом Task с результатом, который появится потом.
Если часто возвращаются FromResult kучше возвращать ValueTask так как нет затрат на размещение в куче.
и солнце б утром не вставало, когда бы не было меня