Haskell STM: основной поток не завершится, пока дочерний поток не завершит выполнение

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

{-# LANGUAGE BangPatterns #-}

module Main
where

import Control.Concurrent.STM 
import Control.Concurrent
import System.IO

nfib :: Int -> Int
nfib n | n <= 2 = 1
       | otherwise = (n1 + n2 )
                     where n1 = nfib (n-1)
                           n2 = nfib (n-2)

type TInt = TVar Int

updateNum :: TInt -> Int -> STM()
updateNum n v = do writeTVar n v

updateTransaction :: TInt -> Int -> IO ()
updateTransaction n v = do 
        atomically $ do
             updateNum n v

main :: IO ()
main = do 
    n <- newTVarIO 10

    forkIO $ do             
        let v = 30
        let !x = nfib v
        updateTransaction n x

    forkIO $ do             
        let v = 15
        let !x = nfib v
        updateTransaction n x

    forkIO $ do             
        let v = 25
        let !x = nfib v
        updateTransaction n x   

    nv <- readTVarIO n
    putStrLn ("Fib number of "  ++ " = " ++ (show nv))  

    nv <-  readTVarIO n
    putStrLn ("Fib number of "  ++ " = " ++ (show nv))

    nv <-  readTVarIO n
    putStrLn ("Fib number of "  ++ " = " ++ (show nv))

Я устал решать эту проблему согласно [ссылке] (Haskell MVar: Как сначала выполнить самое короткое задание?). Я не знаю, правильный ли подход, а также получаю ошибку при попытке напечатать значение TMVar. Вот код: - (nfib такой же, как указано выше)

type TMInt = TMVar Int

updateNum1 :: TMInt -> Int -> STM()
updateNum1 n v = do putTMVar n v

updateTransaction1 :: TMInt -> Int -> IO ()
updateTransaction1 n v = do 
        atomically $ do
            updateNum1 n v

main1 :: IO ()
main1 = do 
    n <- newTMVarIO 0
    forkIO $ do             
        let v = 30
        let !x = nfib v
        updateTransaction1 n x

    forkIO $ do             
        let v = 15
        let !x = nfib v
        updateTransaction1 n x

    forkIO $ do             
        let v = 25
        let !x = nfib v
        updateTransaction1 n x  

    -- t <- takeTMVar n
    -- putStrLn( "result: " ++ (show t))

** Ошибка следующая: -

Couldn't match type `STM' with `IO'
Expected type: IO Int
     Actual type: STM Int
In the return type of a call of `takeTMVar'
In a stmt of a 'do' block: t <- takeTMVar n

Пожалуйста помоги. Спасибо.


person Ammlan Ghosh    schedule 19.01.2015    source источник
comment
В этом коде выполняется несколько STM транзакций, которые выполняют одно чтение или одну запись в TVar. Это выглядит странно. Нет никаких гарантий по порядку этих записей w.r.t. чтения, поэтому любое промежуточное значение может быть прочитано, например. 10,10,10.   -  person chi    schedule 19.01.2015
comment
@Chi, в первом случае (с TVar), если я добавлю задержку потока, например. threadDelay (10^6 * 4) nv <- readTVarIO n putStrLn ("Fib number of " ++ " = " ++ (show nv)), то показывает последнее обновленное значение, т.е. 832040 (Fib 30). В противном случае он показывает значения как 10 610 и 610 (610 — значение Фибоначчи 15).   -  person Ammlan Ghosh    schedule 19.01.2015
comment
Это нормально, если ваша цель — наблюдать за последствиями условий гонки. Тем не менее, мне интересно, почему вы не использовали простые IOVars вместо TVars - вы на самом деле не используете здесь транзакции.   -  person chi    schedule 19.01.2015
comment
@Chi Я использовал TVar вместо IOVar, просто чтобы избежать проблем, связанных с блокировкой (хотя это не применимо, в частности, в этом коде). Я хотел проверить общее время выполнения набора транзакций чтения и записи (в другой программе). Но я не мог этого сделать, так как основной поток завершается до завершения дочерних потоков. Как побороть эту проблему. Есть ли альтернатива кроме TMVar?   -  person Ammlan Ghosh    schedule 19.01.2015
comment
Об этом есть обсуждение в конце hackage. haskell.org/package/base-4.7.0.2/docs/ в Завершение программы   -  person chi    schedule 19.01.2015


Ответы (1)


main1 находится в IO, но takeTMVar возвращает STM Int. Вам нужно запустить транзакцию:

t <- atomically $ takeTMVar n
putStrLn( "result: " ++ (show t))
person Lee    schedule 19.01.2015