Haskell: update two or more tvars atomically probably?
•
Java
Can a transaction update two different tvars atomically? That is, can I combine data structures from many TV dramas to reduce contention? If so, can you provide an example?
Solution
Yes, you can update multiple tvars atomically in one transaction This is the focus of STM If you can't, it's useless
This is a (silly) example of storing tvars in a data structure It simulates a bunch of random concurrent transactions between bank accounts, where each account is just a TVAR integer Account tvars is saved in the map of account ID, and the account ID itself is saved in TVAR so that new accounts can be created immediately
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)
If there is no competition in the transfer process, this should print a total balance of zero at the end
The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
二维码