Как дождаться окончания forM_ при использовании TVar?
Я пишу функцию, где я обрабатываю список с помощью forM_
и добавить результат к TVar
список:
import Control.Concurrent.STM
import Control.Concurrent.STM.TVar
import Control.Concurrent (forkIO)
import Control.Monad (forM_)
insert :: a -> [a] -> [a]
insert = (:) -- stub
my_func_c :: (a -> a) -> [a] -> IO [a]
my_func_c my_function arr = do
res_var <- atomically $ newTVar ([]::[a])
forkIO $ forM_ arr $ \x -> atomically $ do
let y = id $! my_function x
modifyTVar res_var (insert y)
atomically $ readTVar res_var
Результат всегда пуст, если я скомпилирую его -threaded
, Как можно ждать окончания потоков? Я не могу использовать MVar
или же Async
, Я должен решить эту проблему, используя TVar
или другие структуры данных, основанные на TVar
1 ответ
Решение
Идиоматическим решением было бы использовать TMVar
s:
my_func_c :: (a -> a) -> [a] -> IO [a]
my_func_c my_function arr = do
res_var <- atomically $ newTVar []
finished <- atomically $ newEmptyTMVar
forkIO $ do
forM_ arr $ \x -> atomically $ do
let y = id $! my_function x
modifyTVar res_var (insert y)
atomically $ putTMVar finished ()
atomically $ takeTMVar finished >> readTVar res_var
но если вы действительно можете использовать толькоTVar
с, вы можете моделироватьTMVar ()
с TVar Bool
:
my_func_c :: (a -> a) -> [a] -> IO [a]
my_func_c my_function arr = do
res_var <- atomically $ newTVar []
finished <- atomically $ newTVar False
forkIO $ do
forM_ arr $ \x -> atomically $ do
let y = id $! my_function x
modifyTVar res_var (insert y)
atomically $ writeTVar finished True
atomically $ waitTVar finished >> readTVar res_var
waitTVar :: TVar Bool -> STM ()
waitTVar tv = do
finished <- readTVar tv
unless finished retry
Под капотом,TMVar a
это простоTVar (Maybe a)
(так TMVar ()
~TVar (Maybe ())
~ TVar Bool
) с takeTMVar
делатьreadTVar
иretry
Таким образом, два вышеупомянутых решения являются операционно полностью эквивалентными.