gpt4 book ai didi

Haskell:以原子方式更新两个或多个 TVar。可能的?

转载 作者:行者123 更新时间:2023-12-02 16:45:36 26 4
gpt4 key购买 nike

一个事务可以以原子方式更新两个不同的TVar吗?即我可以用大量 TVar 组成数据结构来减少争用吗?如果是的话,您能举个例子吗?

最佳答案

Can one transaction update two different TVars in an atomic way?

是的,您可以在一次事务中自动更新多个 TVar。这就是 STM 的全部意义所在。如果你不能,它就不会很有用。

Can I compose data structures out of lots of TVars to reduce contention? If so, could you provide an example?

这是一个在数据结构中存储 TVar 的(有点愚蠢的)示例。它模拟银行帐户之间的一堆随机并发交易,其中每个帐户只是一个 TVar Integer。帐户 TVar 保存在帐户 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)

如果传输过程中没有竞争条件,那么最后应该打印出总余额为零。

关于Haskell:以原子方式更新两个或多个 TVar。可能的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10099815/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com