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

Haskell:以原子方式更新两个或多个TVAR。可能的,haskell,concurrency,stm,tvar,Haskell,Concurrency,Stm,Tvar,一个事务能否以原子方式更新两个不同的TVars?i、 e.我可以用大量的TVars组成数据结构以减少争用吗?如果是这样,你能举个例子吗?事务是完全原子的;如果它修改了多个TVars,那么这两个更改将在原子上单独发生。在单个原子块中运行的任何内容都是单个事务。例如: swap :: (Num a) => TVar a -> TVar a -> STM () swap v1 v2 = do a <- readTVar v1 b <- readTVar v

一个事务能否以原子方式更新两个不同的
TVar
s?i、 e.我可以用大量的
TVar
s组成数据结构以减少争用吗?如果是这样,你能举个例子吗?

事务是完全原子的;如果它修改了多个
TVar
s,那么这两个更改将在原子上单独发生。在单个
原子
块中运行的任何内容都是单个事务。例如:

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::(Num a)=>TVar a->TVar a->STM()
交换v1 v2=do
A.
一个事务能否以原子方式更新两个不同的TVAR

是的,您可以在一个事务中自动更新多个TVAR。这就是STM的要点。如果你不能,那就没什么用了

我可以用大量TVAR组成数据结构以减少争用吗?如果是,你能举个例子吗

下面是一个(有点傻)在数据结构中存储TVAR的示例。它模拟银行账户之间的一系列随机并发交易,其中每个账户只是一个
TVar整数
。帐户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)
导入控制。并发
导入控制.Concurrent.MVar
导入控制.Concurrent.STM
进口管制
导入系统。随机
导入符合条件的数据。映射为映射
类型AccountId=Int
类型账户=TVar美元
类型美元=整数
类型Bank=TVar(Map.Map AccountId Account)
账户数量=20
线程数=100
transactionsPerThread=100
最大金额=1000
--按ID获取帐户,如果不存在,则创建新的空帐户
getAccount::银行->账户ID->STM账户
getAccount银行帐户ID=do
帐户返回帐户
无事可做
账户->账户->STM()
转账金额从到=当(从/=到)$do

从漂亮的回答中获得平衡。我有一个问题。
块能否以原子方式从
随机事务
移动到
传输
?我的意思是从
randomTransaction
中删除
atomicly
,并用
atomicly
包装整个
transfer
。这是一个完美的答案!谢谢@hammar。