Способ формирования "выбора" на MVars без опроса

У меня есть два MVars (ну MVar и Chan). Мне нужно вытащить вещи из Чан и обработать их до тех пор, пока другой MVar больше не будет пустым. Мое идеальное решение было бы чем-то вроде функции UNIX select, в которой я передаю список (предположительно пустых) MVars и блоков потоков до тех пор, пока один из них не будет заполнен, а затем вернет полный MVar. Попытайтесь, чтобы я мог думать о том, чтобы не делать этого заново, неоднократно опросив каждого MVar с помощью isEmptyMVar, пока не получу false. Это кажется неэффективным.

Другая мысль заключалась в том, чтобы использовать throwTo, но он прерывает то, что когда-либо происходит в потоке, и мне нужно закончить обработку работы из Chan атомным способом.

Последнее, что я набираю, - это создать новый forkIO для каждого MVar, который пытается прочитать его MVar, а затем заполнить вновь созданный MVar своим собственным экземпляром. Исходный поток затем может блокировать этот MVar. Являются ли потоки Haskell дешевыми, чтобы их можно было запустить?

Ответы

Ответ 1

Нити Haskell очень дешевы, поэтому вы можете решить их таким образом, но похоже, что STM лучше подходит для вашей проблемы. С помощью STM вы можете делать

do var <- atomically (takeTMVar a `orElse` takeTMVar b)
   ... do stuff with var

Из-за поведения retry и orElse, этот код пытается получить a, а если это не удается, получите b. Если оба они не работают, они блокируются до тех пор, пока ни один из них не будет обновлен, и повторит попытку.

Вы даже можете использовать это, чтобы создать свою собственную рудиментарную версию select:

select :: [TMVar a] -> STM a
select = foldr1 orElse . map takeTMVar

Ответ 2

Как насчет использования версий STM, TChan и TVar, с поведением retry и orElse?

Реализация select - одна из хороших возможностей STM. Из "Сложных транзакций памяти":

Помимо этого, мы также предоставляем orElse, что позволяет их составлять как альтернативы, так что вторая выполняется, если первые попытки (раздел 3.4). Эта способность позволяет нитьм ждать многих вещей одновременно, например, Unix выбирает системный вызов - за исключением того, что orElse правильно складывается, тогда как выбор не работает.