2012-04-11 4 views

ответ

7

Может ли одна транзакция обновить два разных телевизора в атомном режиме?

Да, вы можете обновлять несколько ТВАР атомарно за одну транзакцию. Это всего лишь точка STM. Было бы не очень полезно, если бы вы не смогли.

Могу ли я создавать структуры данных из множества ТВАР, чтобы уменьшить конфликт? Если да, можете ли вы привести пример?

Вот (несколько глупый) пример хранения ТВАР в структуре данных. Он имитирует кучу случайных параллельных транзакций между счетами в банке, где каждая учетная запись всего лишь TVar Integer. Учетные TVars хранятся на карте из идентификаторов учетных записей, которые сами хранятся в TVar, так что новые учетные записи могут создаваться «на лету».

import Control.Concurrent 
import Control.Concurrent.MVar 
import Control.Concurrent.STM 
import Control.Monad 
import System.Random 

import qualified Data.Map as Map 

type AccountId = Int 
type Account = TVar Dollars 
type Dollars = Integer 
type Bank = TVar (Map.Map AccountId Account) 

numberOfAccounts = 20 
threads = 100 
transactionsPerThread = 100 
maxAmount = 1000 

-- Get account by ID, create new empty account if it didn't exist 
getAccount :: Bank -> AccountId -> STM Account 
getAccount bank accountId = do 
    accounts <- readTVar bank 
    case Map.lookup accountId accounts of 
    Just account -> return account 
    Nothing -> do 
     account <- newTVar 0 
     writeTVar bank $ Map.insert accountId account accounts 
     return account 

-- Transfer amount between two accounts (accounts can go negative) 
transfer :: Dollars -> Account -> Account -> STM() 
transfer amount from to = when (from /= to) $ do 
    balanceFrom <- readTVar from 
    balanceTo <- readTVar to 
    writeTVar from $! balanceFrom - amount 
    writeTVar to $! balanceTo + amount 

randomTransaction :: Bank -> IO() 
randomTransaction bank = do 
    -- Make a random transaction 
    fromId <- randomRIO (1, numberOfAccounts) 
    toId <- randomRIO (1, numberOfAccounts) 
    amount <- randomRIO (1, maxAmount) 

    -- Perform it atomically 
    atomically $ do 
    from <- getAccount bank fromId 
    to <- getAccount bank toId 
    transfer amount from to 

main = do 
    bank <- newTVarIO Map.empty 

    -- Start some worker threads to each do a number of random transactions 
    workers <- replicateM threads $ do 
    done <- newEmptyMVar 
    forkIO $ do 
     replicateM_ transactionsPerThread $ randomTransaction bank 
     putMVar done() 
    return done 

    -- Wait for worker threads to finish 
    mapM_ takeMVar workers 

    -- Print list of accounts and total bank balance (which should be zero) 
    summary <- atomically $ do 
    accounts <- readTVar bank 
    forM (Map.assocs accounts) $ \(accountId, account) -> do 
     balance <- readTVar account 
     return (accountId, balance) 

    mapM_ print summary 
    putStrLn "----------------" 
    putStrLn $ "TOTAL BALANCE: " ++ show (sum $ map snd summary) 

Это должно напечатать полный баланс нуля в конце, если во время передач не было условий гонки.

+0

Хороший ответ. У меня есть один вопрос. Может ли блок 'atomically' быть перемещен из' randomTransaction' в 'transfer'? Я имею в виду удаление «атомарно» из «randomTransaction» и обертывание всего тела «переноса» с помощью «атомарно». –

6

Сделка полностью атомная; если он изменяет несколько TVar с, оба изменения будут происходить вместе, атомарно, изолированно. Все, что выполняется в одном блоке atomically, представляет собой единую транзакцию. Например:

swap :: (Num a) => TVar a -> TVar a -> STM() 
swap v1 v2 = do 
    a <- readTVar v1 
    b <- readTVar v2 
    writeTVar v1 b 
    writeTVar v2 a 

Здесь swap a b будет атомарно поменять два TVar с. Таким образом, одно из основных преимуществ STM заключается в том, что атомарные транзакции составляют.

Смежные вопросы