加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

Haskell:原子地更新两个或更多TVar.可能?

发布时间:2020-12-15 05:08:14 所属栏目:Java 来源:网络整理
导读:一个事务可以以原子方式更新两个不同的TVar吗?即,我可以从许多电视剧中组合数据结构以减少争用吗?如果是这样,你能提供一个例子吗? 解决方法 Can one transaction update two different TVars in an atomic way? 是的,您可以在一次交易中以原子方式更新多
一个事务可以以原子方式更新两个不同的TVar吗?即,我可以从许多电视剧中组合数据结构以减少争用吗?如果是这样,你能提供一个例子吗?

解决方法

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

是的,您可以在一次交易中以原子方式更新多个TVars.这就是STM的重点.如果你做不到,那就没用了.

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

这是在数据结构中存储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)

如果在转移过程中没有竞争条件,这应该在最后打印总余额为零.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读