Haskell:原子地更新两个或更多TVar.可能?
发布时间:2020-12-15 05:08:14 所属栏目:Java 来源:网络整理
导读:一个事务可以以原子方式更新两个不同的TVar吗?即,我可以从许多电视剧中组合数据结构以减少争用吗?如果是这样,你能提供一个例子吗? 解决方法 Can one transaction update two different TVars in an atomic way? 是的,您可以在一次交易中以原子方式更新多
一个事务可以以原子方式更新两个不同的TVar吗?即,我可以从许多电视剧中组合数据结构以减少争用吗?如果是这样,你能提供一个例子吗?
解决方法
是的,您可以在一次交易中以原子方式更新多个TVars.这就是STM的重点.如果你做不到,那就没用了.
这是在数据结构中存储TVars的(有点愚蠢)示例.它模拟了银行账户之间的一堆随机并发交易,其中每个账户只是一个TVar Integer.帐户TVars保存在帐户ID的地图中,帐户ID本身保存在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) 如果在转移过程中没有竞争条件,这应该在最后打印总余额为零. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |