Haskell STM: как сохранить ThreadID в соответствии с их последовательностью выполнения

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

module Main
where
import Control.Parallel
import Control.Concurrent.STM
import Control.Concurrent
import System.Random 
import Control.Monad
import Data.IORef
import System.IO

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


type TInt = TVar Int


updateNum :: TInt -> Int -> STM()
updateNum n v = do x1 <- readTVar n
                   let y = nfib v
                   x2 <- readTVar n
                   if x1 == x2
                   then writeTVar n y   
                   else retry

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

incR :: IORef Int -> Int -> IO ()
incR r x = do { v <- readIORef r;                    
      writeIORef r (v - x) }

main :: IO ()
main = do 
    n <- newTVarIO 10
    r <- newIORef 40;
    forM_ [1..10] (\i -> do 
                     incR r i
                     ;v <- readIORef r
                     ;forkIO (updateTransaction n v)
                    )

Я хочу сохранить [TreadID, FibNo] в списке для всех потоков в соответствии с их выполнением. Предположим, T1 выполнил Fib30, T2 Fib35, T3->32 и T4->40. И если последовательность фиксации потоков, таких как T1, T3, T2 и T4, тогда я хочу сохранить T1-35, T3-32, t2-35, t4-40 в списке.

Изменить: по предложению @MathematicalOrchid я изменил updateTrasaction следующим образом:

updateTransaction :: MVar [(ThreadId, Int)] -> TInt -> Int -> IO ()
updateTransaction mvar n v = do
  tid <- myThreadId
  atomically $ updateNum n v
  list <- takeMVar mvar
  putMVar mvar $ list ++ [(tid, v)]

Теперь я пытаюсь распечатать значения из этого списка в основном

main :: IO ()
main = do 
  ...
  ...
  m <- newEmptyMVar
  ...
  ...
  mv <- readMVar m
  putStrLn ("ThreadId, FibVal : "  ++ " = " ++ (show mv)) 

В момент исполнения. Значения MVar не могут быть прочитаны и выдает ошибку

Exception: thread blocked indefinitely in an MVar operation

Что делать? Заранее спасибо.


person Ammlan Ghosh    schedule 03.12.2014    source источник
comment
Вы знаете, что ваша реализация Фибоначчи действительно медленная? Или вы хотите использовать эту функцию в качестве теста?   -  person Zeta    schedule 03.12.2014
comment
Фибоначчи функционирует как тест. Например, поскольку Фибоначчи 35 займет больше времени, чем Фибоначчи 25, и, следовательно, время выполнения для потока 1 (Фиб 35) займет больше времени, чем для потока 2 (Фиб 25). Таким образом, сначала будет выполняться tread2, а затем ad1 (из-за «повторной попытки»).   -  person Ammlan Ghosh    schedule 03.12.2014
comment
x1 <- readTVar n; …; x2 <- readTVar n? Вы в курсе, что это совершенно не обязательно?   -  person Zeta    schedule 03.12.2014
comment
Что заставляет вас думать, что ветвь else когда-нибудь будет выполнена? Если x1 /= x2, то GHC автоматически прервет транзакцию.   -  person MathematicalOrchid    schedule 03.12.2014
comment
@Zeta, пожалуйста, помогите мне развеять мои сомнения. Я сохранил значение n сначала в x1, а затем в x2. Я сделал так, чтобы проверить несоответствие данных, чтобы на основании этого записать в TVar или повторить попытку. Может ли Haskell STM автоматически повторить попытку в погоне за несогласованностью данных. Насколько я знаю, в этом случае он должен прерваться.   -  person Ammlan Ghosh    schedule 04.12.2014
comment
@AmmlanGhosh: вы прочитали chimera.labs.oreilly.com/books/1230000000929 /ch10.html, верно? Когда вычисление STM выполняется таким образом, оно называется транзакцией, поскольку вся операция выполняется атомарно по отношению к остальной части программы. Внутри транзакции никакие TVar не могут изменить свое значение, за исключением случаев, когда это изменение произошло внутри этой транзакции.   -  person Zeta    schedule 04.12.2014


Ответы (1)


Вы хотели что-то вроде

updateTransaction :: TInt -> Int -> IO ()
updateTransaction n v = do
  tid <- myThreadId
  putStrLn $ "Start " ++ show tid
  atomically $ updateNum n v
  putStrLn $ "End " ++ show tid

Или, возможно, что-то вроде

updateTransaction :: TInt -> Int -> IO ThreadId
updateTransaction n v = do
  atomically $ updateNum n v
  myThreadId

и изменить forM_ на forM?


Также эта часть:

do
  x1 <- readTVar n
  ...
  x2 <- readTVar n
  if x1 == x2 ...

Если x1 /= x2, GHC автоматически прервет и перезапустит вашу транзакцию. Вам не нужно вручную проверять это самостоятельно. Действительно, ветвь else может никогда не выполняться. Это своего рода точка STM; вашей транзакции будет казаться, что никто другой не изменяет просматриваемые вами данные, поэтому вам никогда не придется беспокоиться о параллельной записи.


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

updateTransaction :: MVar [(ThreadId, Int)] -> TInt -> Int -> IO ()
updateTransaction mvar n v = do
  tid <- myThreadId
  fib <- atomically $ updateNum n v
  list <- takeMVar mvar
  putMVar mvar $ list ++ [(tid, fib)]

(Очевидно, вы должны заставить updateNum возвращать вычисленное число.)

person MathematicalOrchid    schedule 03.12.2014
comment
Спасибо вам большое за ваш ответ. Вы правильно сказали, что x1 /= x2 прервет транзакцию. Транзакция может быть выполнена или может быть прервана и перезапущена снова. Я хочу сохранить [TreadID, FibNo] в списке для всех потоков в соответствии с их выполнением. Предположим, что T1 выполнил Fib30, T2 Fib35, T3->32 и T4->40. И если последовательность фиксации потоков, таких как T1, T3, T2 и T4, тогда я хочу сохранить T1-35, T3-32, t2-35, t4-40 в списке. - person Ammlan Ghosh; 04.12.2014
comment
Спасибо @MathematicalOrchid. Дай мне попробовать. - person Ammlan Ghosh; 04.12.2014
comment
Спасибо, @MathematicalOrchid. По вашему совету я изменил upateTransaction. [т.е. updateTransaction :: MVar [(ThreadId, Int)] -> TInt -> Int -> IO () ... .... putMVar mvar $ list ++ [(tid, v)] (я хочу сохранить v вместо fib, соответственно измененного). В основном я объявил m <- newEmptyMVar и назвал forkIO (updateTransaction m n v). Но я не смог напечатать значения m в файле main. Пожалуйста помоги. - person Ammlan Ghosh; 09.12.2014
comment
Чтобы уточнить: Вы не можете решить, что писать? Или вы что-то написали, и это не соответствует вашим ожиданиям? - person MathematicalOrchid; 09.12.2014
comment
Кому: @MathematicalOrchid; Я отредактировал свой вопрос и добавил код для вашего прочтения. - person Ammlan Ghosh; 12.12.2014
comment
Вы создали пустой MVar. Вам нужно создать тот, который начинается с []. - person MathematicalOrchid; 12.12.2014
comment
Спасибо за вашу помощь. Я исправил свой код как m <- newEmptyMVar putMVar m [] - person Ammlan Ghosh; 12.12.2014