Цикл core.async заблокирован в ожидании чтения из канала

скажем, у меня есть канал out (chan). Мне нужно взять значения, которые помещаются в канал, и добавить их. Количество значений не определено (поэтому нельзя использовать традиционный цикл с конечным регистром (<! out)) и исходит от внешнего ввода-вывода. Я использую фиксированный timeout с alts!, но это не лучший способ решения проблемы. На данный момент у меня есть следующее (которое я получил от https://gist.github.com/schaueho/5726a96641693dce3e47)

(go-loop
      [[v ch] (alts! [out (timeout 1000)])
       acc 0]
      (if-not v
        (do (close! out)
            (deliver p acc))
        (do
          (>! task-ch (as/progress-tick))
          (recur (alts! [out (timeout 1000)]) (+ acc v)))))

Проблема, с которой я столкнулся, заключается в том, что тайм-аута в 1000 иногда бывает недостаточно, и это приводит к преждевременному завершению цикла перехода (поскольку для завершения операции ввода-вывода и помещения значения в канал out может потребоваться более 1000 мс). Я не думаю, что увеличение значения тайм-аута — хорошая идея, так как это может заставить меня ждать дольше, чем необходимо.

Каков наилучший способ гарантировать все операции чтения из выходного канала и правильный выход из цикла?

Обновление:

Почему я использую тайм-аут? Поскольку количество значений, помещаемых в канал, не является фиксированным; что означает, что я не могу создать случай выхода. Без случая выхода цикл перехода будет бесконечно ждать ((<! out)) значений, которые будут помещены в выходной канал. Если у вас есть решение без тайм-аута, это было бы действительно здорово.

Как я узнаю, что прочитал последнее значение? Нет. Это проблема. Вот почему я использую тайм-аут и альт!! чтобы выйти из цикла.

Что вы хотите сделать с результатом? Простое добавление на данный момент. Однако это не главное.

Окончательное обновление:

Я нашел способ получить количество значений, с которыми я буду иметь дело. Поэтому я изменил свою логику, чтобы использовать это. Я все еще собираюсь использовать тайм-аут и альты! для предотвращения блокировки.

(go-loop
     [[v _] (alts! [out (timeout 1000)])
      i 0
      acc 0]
      (if (and v (not= n i))
        (do
          (>! task-ch (as/progress-tick))
          (recur (alts! [out (timeout 1000)]) (inc i) (+ acc v)))
        (do (close! out)
            (deliver p* (if (= n i) acc nil)))))

person Lordking    schedule 15.02.2018    source источник
comment
а. Зачем вообще нужен тайм-аут? б. Как узнать, прочитали ли вы последнее значение? в. Что вы хотите сделать с результатом?   -  person orestis    schedule 15.02.2018
comment
alts!! принимает вектор каналов и считывает значение из первой успешной операции канала. Если канал out получает значения до истечения времени ожидания, я могу продолжить цикл. Если я получаю канал тайм-аута, это означает, что я могу выйти из цикла. Я не нашел другого способа выйти из цикла и, следовательно, мой вопрос.   -  person Lordking    schedule 15.02.2018
comment
Нам не хватает важной информации. Если вы не знаете, сколько значений поступает, как вы узнаете, когда вы закончите?   -  person orestis    schedule 15.02.2018
comment
именно это я и спрашиваю. Я не знаю, сколько значений поступает. Вот почему я не могу использовать случай выхода, чтобы определить, когда я закончу, и прибегнул к тайм-ауту с альтами! для выхода из цикла чтения. Если бы это было n значений, я мог бы просто использовать (dotimes [v (<! out) i 1] ...) и использовать i, чтобы определить, что я достиг конца.   -  person Lordking    schedule 15.02.2018
comment
Выброшу шляпу на ринг и скажу, что вы подходите к этому в корне неправильно. Вы не можете суммировать бесконечный поток. Каждый раз, когда нужно использовать тайм-аут, в 95% случаев это указывает на ошибку в архитектуре. Выходы на основе времени общеизвестно ненадежны и обычно являются пластырем при неудачном выборе дизайна.   -  person Josh    schedule 19.02.2018
comment
Ага. это то, что я понял. Однако я не имел дело с бесконечным потоком. Это всегда имело конец. Он просто менялся в каждом заезде. Как только я понял, с каким количеством val я буду работать, создание цикла чтения стало довольно простым. Хотя просто полагаться на счет у меня не очень хорошо получалось. Если внутри блока go, который вставляет данные, произойдет что-то плохое, цикл чтения застрянет в ожидании. Тайм-аут помог мне избежать этих случаев.   -  person Lordking    schedule 21.02.2018


Ответы (2)


Я думаю, что ваша проблема немного выше в вашем дизайне, а не в основном асинхронном:

С одной стороны, в канал поступает неопределенное количество значений — может быть 0, может быть 10, может быть 1 000 000.

С другой стороны, вы хотите прочитать все из них, выполнить некоторые вычисления, а затем вернуться. Это невозможно сделать — если нет другого сигнала, который вы можете использовать, чтобы сказать: «Думаю, я закончил».

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

Обновление: есть ли у вас доступ к «восходящему» вводу-выводу? Можете ли вы поместить сигнальное значение (например, что-то вроде ::closed) в канал, когда операция ввода-вывода завершится?

person orestis    schedule 15.02.2018
comment
Благодарю. решение, которое у меня есть, работает, так как у меня есть тайм-аут самой операции ввода-вывода. Мне просто нужно было посмотреть, есть ли лучший способ. Кроме того, как бы вы упростили то, что у меня есть? - person Lordking; 15.02.2018
comment
Я попробовал подход :closed. Однако я использую блоки go (с асинхронным вводом-выводом) для вставки значений в канал, поэтому я не могу обеспечить порядок, в котором значения вставляются в него. это означает, что есть действительно хороший шанс, что я получу :closed val до конца. - person Lordking; 15.02.2018
comment
Кажется, вы приравниваете тайм-аут ввода-вывода к тому, что ввод-вывод выполнен, но, основываясь на том, что вы поделились до сих пор, это не гарантируется правильно. Весь этот вопрос кажется проблемой XY — я не думаю, что вы получите лучший ответ, чем то, что я и @tecoup уже поделились, если вы не подниметесь на один уровень выше и не опишите, чего вы пытаетесь достичь. - person orestis; 15.02.2018

«Лучший» способ - дождаться либо специального сообщения об окончании пакета от out, либо закрытия out отправителем, чтобы отметить конец входных данных.

В любом случае решение зависит от отправителя, который сообщает что-то о входных данных.

person tecoup    schedule 15.02.2018