LINUX.ORG.RU

Как вы пишете асинхронный, интерактивный код?

 


0

3

Имеется привычная ситуация: UI, в котором пользователь бездумно жмакает кнопочки, которые в свою очередь запускают асинхронные/параллельные задачи. Это может быть как и GUI, так и сайт, посылающий запросы серверу.

А теперь начинается интересное:

  1. Любое действие должно быть отменяемым (не убивание потока, а штатное завершение). Большинство async кода что я видел, не поддерживает такую простую фичу. Видимо вебсерверам это не интересно. Даже в Go, с его горутинами, это достигается велосипедами, а не средствами языка.
  2. Любое действие может быть продолжено. Это немного похоже на предыдущую задачу, но с тем нюансом, что у нас задача представляет собой не loop {}, а цепочку действий.
  3. Задача может общаться с родителем. Запрашивать новые данные, подтверждать действия.
  4. Задача может использовать глобальный контекст. То есть живёт не сама по себе.
  5. Задача может запускать свои асинхронные задачи и на неё распространяются те же требования, что и выше.
  6. Это не должно превратиться в callback hell.

Язык роли не играет. Производительность тоже. Интересует сам алгоритм.

★★★★★

Elixir c агентами, для асинхронного программирования ничего лучше нет.

Lzzz
()

То, что вы описываете, выглядит как актор.

ugoday ★★★★★
()

Как вы пишете асинхронный, интерактивный код?

Не пишу такой код

gaiponie
()

ну как вариант может машина состояний которая дирижирует асинхронно приходящими состояниями от каждого источника состояний с уникальным идентификатором, тогда не будет нужды какие-то потоки убивать можно вообще будет просто пользоваться некоторым пулом потоков в небольшим запасом на случай пикового клацанья по кнопкам в истерике, так же можно продумать постановку в очередь если вдруг пик будет слишком резким чтобы было время дозапустить еще потоков для обработки. Вообще кажется тут действительно кто на что горазд, кому как больше нравится тот так и делает. Единственное что я не понял почему в golang это должно быть каким-то изкоробочным решением, просто на правах хейта golang? :)

abcq ★★
()
Ответ на: комментарий от RazrFalcon

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

А вообще, в managed языках как-то даже не интересно. Я так в питоне делал через settrace, но оверхед, конечно, получался знатный.

WitcherGeralt ★★
()

Языки на BEAM? Вроде все пункты присутствуют.

theNamelessOne ★★★★★
()
Последнее исправление: theNamelessOne (всего исправлений: 1)

может что-то почитать на тему архитектуры распределенных систем?

в общем случае это процессы или треды, общающиеся сообщениями по каналам посредством p2p или бродкастом. а никакие не колбеки и прочие хехе замыкания.

alysnix ★★★
()
Ответ на: комментарий от Princesska

Если совсем асинхронные, то нет - лучше потоки. Если их нет, то можно реализовать на продолжениях, но если есть, проще использовать готовые.

А если в стиле «вернуться из асинхронного вызова в вызывающий код», то продолжения рулят, но именно для этой задачи во многих языках сделали async/await.

А так всё, что перечислено, в Racket реализовано стандартной библиотекой:

  1. break-thread
  2. thread-suspend и thread-resume
  3. thread-send и thread-receive
  4. thread видит все переменные в контексте запуска
  5. может
  6. не превращается
monk ★★★★★
()

погляди как в винде эвенты обрабатываются (кстати там можно TCP ловить эвентами, что ОЧЕНЬ удобно)

dem ★★
()
Ответ на: комментарий от monk

именно для этой задачи во многих языках сделали async/await

К сожалению, я так и не въехал в них. Времени всё не было. Есть какое-то толковое объяснение?

RazrFalcon ★★★★★
() автор топика

Ты описал акторы.

Reset ★★★★★
()

Любое действие должно быть отменяемым (не убивание потока, а штатное завершение). Большинство async кода что я видел, не поддерживает такую простую фичу. Видимо вебсерверам это не интересно. Даже в Go, с его горутинами, это достигается велосипедами, а не средствами языка.

Ты или пишешь код, который каждый такт смотрит во флажок, или пишешь изолированный с данными код, который можно грохнуть.

Только на эрланг не смотри, потому как увидишь, как можно сделать по-человечески и будешь страдать от всего остального.

max_lapshin ★★★★★
()

Только если надо оптимизировать по скорости/обеспечить отзывчивость. В противном случае это вагон багов и геморой.

peregrine ★★★★★
()
Ответ на: комментарий от WitcherGeralt

Покажи csp на го. Если ты про отправку сообщений, то тут ничего нового и в моей задаче это никак не поможет.

RazrFalcon ★★★★★
() автор топика
Последнее исправление: RazrFalcon (всего исправлений: 1)
Ответ на: комментарий от RazrFalcon

Причём здесь твоя задача? Я тебе одним словом описал всю суть async/await. Вообще безотносительно неё.

WitcherGeralt ★★
()
Ответ на: комментарий от RazrFalcon

https://learn.javascript.ru/async-await

или

https://rust-lang.github.io/async-book/03_async_await/01_chapter.html

Если на пальцах, то async при определении функции указывает, что функция при запуске работает в отдельном потоке, а вызвавший её код тут же продолжает выполнение.

Если внутри такой функции запускается другая async функция, то её можно подождать (и получить её результат) написав перед вызовом await.

Соответственно, основной поток не блокируется такими функциями, а код выполняется по возможности параллельно.

В растовской реализации есть странность: при вызове асинхронной функции из другой асинхронной функции новый поток автоматически не запускается. Есть костыль: https://rust-lang.github.io/async-book/06_multiple_futures/02_join.html

В реализации C# у результата асинхронной функции есть поле Result, при обращении к которому выполнение вызывающего блокируется до окончания вычисления асинхронной функции. Эдакий thread-join. В целом, вариант C# наиболее удобный для использования.

monk ★★★★★
()
Ответ на: комментарий от WitcherGeralt

Как запустить n горутин последовательно, с возможностью остановить выполнение по условию?

Псевдокод:

await fun1()
let result = await fun2()
if result.is_ok()
    await fun3()
else 
    await fun4()

При этом каждая funN может завершится по глобальному флагу. То есть я могу жмакнуть Cancel пока выполняется fun2 и код не дойдёт до if result.is_ok.

RazrFalcon ★★★★★
() автор топика
Ответ на: комментарий от monk

Растовская книга не дописана. Но я её уже читал и так ничего и не понял.

Если на пальцах

Это как раз понятно. Не ясно как прервать await. Передавать в каждую функцию «канал»? Будет выглядеть жутко.

То есть для примера, я посылаю запрос и он висит 5 мин. Я хочу его отменить. Как это сделать, если прога уже и так висит в await.

RazrFalcon ★★★★★
() автор топика
Ответ на: комментарий от RazrFalcon
func fun2(s int32) <-chan int32 {
    r := make(chan int32)
    go func() {
        defer close(r)
        // здесь вычисляем result
        r <- result
    }()
    return r
}

func fun1() {
    result := <-fun2()
    if result = 1 {
      return <-fun3()
    } else {
      return <-fun4()
    }
}
monk ★★★★★
()
Ответ на: комментарий от RazrFalcon

Но я не предлагал тебе решать твою задачу на Go.

С твоими, как всегда, про иворечащими друг другу требованиями, пришлось бы с ног до головы обмазаться каналами.

Совокупность пунктов 2 и 3 питонические таски тоже исключает.

WitcherGeralt ★★
()
Ответ на: комментарий от monk

По его требованиям ты должен на каждую горутину ещё и канал с селектом для прерывания воткнуть.

WitcherGeralt ★★
()
Последнее исправление: WitcherGeralt (всего исправлений: 1)
Ответ на: комментарий от WitcherGeralt

Требования диктует реальность, а не я. Пользователь стартонул продолжительную, многоступенчатую задачу и у него в статусбаре бежит прогресс. И тут он передумал и жмакает отмена. Мне нужно всё штатно прервать.

RazrFalcon ★★★★★
() автор топика
Ответ на: комментарий от RazrFalcon

Значит на каждой ступени должна быть обработка прерывания.

Как я уже сказал выше, в питоне это можно сделать с помощью settrace на потоке. Но это колхоз.

WitcherGeralt ★★
()
Ответ на: комментарий от monk

Так вот я и не пойму для чего они тогда предназначены. Такую истерию устроили когда в Rust добавляли. А я вообще не понимаю какой от них толк, если прерывать/продолжать нельзя.

Полностью готовое решение вот

В racket какие-то свои потоки или вы намекаете что всё нужно делать самому?

RazrFalcon ★★★★★
() автор топика
Последнее исправление: RazrFalcon (всего исправлений: 1)
Ответ на: комментарий от WitcherGeralt

Ну так у меня так и сделано. Но это callback hell.

RazrFalcon ★★★★★
() автор топика
Ответ на: комментарий от RazrFalcon

Так вот я и не пойму для чего они тогда предназначены

Вот я тебе и говорю, что это такая чуть более благолепная и асинхронная форма goto. Оно нужно чтобы прыгать туда-сюда, но чтобы код при этом выглядел последовательным. Просто способ избавиться от коллбеков, не более того.

WitcherGeralt ★★
()
Ответ на: комментарий от RazrFalcon

Пользователь стартонул продолжительную, многоступенчатую задачу и у него в статусбаре бежит прогресс. И тут он передумал и жмакает отмена. Мне нужно всё штатно прервать.

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

сам тред ты прервать не можешь, нет такого корректного способа, ты можешь только правильно завершить тот свой код, который этот тред исполняет. а это обычно либо проверка глобального флага, либо проверка собственной переменной треда, типа bool _stop. но собственные переменные у тредов есть там, где они либо реализованы тобой, либо либой, и явно тобой используются. а если ты пускаешь асинхронные функции, то треды тут скрыты, надо только использовать внешний флаг и функция должна его проверять.

alysnix ★★★
()
Ответ на: комментарий от RazrFalcon

Свои потоки. Реализованы как зелёные, что снимает большую часть проблем с атомарностью операций.

Также всё перечисленное легко реализовать на Erlang. Процессы асинхронные, можно останавливать через kill. Внутри процесса можно ловить kill через receive и нормально обрабатывать завершение процесса.

monk ★★★★★
()
Ответ на: комментарий от alysnix

сам тред ты прервать не можешь

Это если о нативе говорить. В managed языках можно, конечно. Там по сути и так есть и цикл, обрабатывающий фреймы/кванты твоего кода, и флаги.

WitcherGeralt ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.