我正在Haskell中编写MUD服务器(MUD =多用户地牢:基本上是一个多用户文本冒险/角色扮演游戏).游戏世界数据/状态以大约15种不同的IntMaps表示.我的monad变换器堆栈看起来像这样:ReaderT MudData IO,其中MudData类型是包含IntMaps 的记录类型,每个都在自己的TVar(我使用STM进行并发):
data MudData = MudData { _armorTblTVar :: TVar (IntMap Armor)
, _clothingTblTVar :: TVar (IntMap Clothing)
, _coinsTblTVar :: TVar (IntMap Coins)
Run Code Online (Sandbox Code Playgroud)
...等等.(我正在使用镜头,因此是下划线.)
有些功能需要某些功能IntMap,而其他功能需要其他功能.因此,每个IntMap都有自己的TVar粒度.
但是,我的代码中出现了一种模式.在处理播放器命令的函数中,我需要TVar在STM monad中读取(有时稍后写入)我的s.因此,这些函数最终在其where块中定义了STM帮助器.这些STM助手通常readTVar在其中有相当多的操作,因为大多数命令需要访问少数几个IntMaps.此外,给定命令的函数可以调用许多纯辅助函数,这些函数也需要一些或全部IntMaps.因此,这些纯辅助函数有时会占用大量参数(有时超过10).
所以,我的代码变得"乱七八糟",有很多带有大量参数的readTVar表达式和函数.以下是我的问题:这是代码味道吗?我错过了一些可以使我的代码更优雅的抽象吗?有没有更理想的方法来构建我的数据/代码?
谢谢!
我正在考虑使用TVar在Web应用程序中存储某些状态(可以在重新启动时重新创建).然而,TVar的争论方面关注我.似乎频繁的短期交易可以通过不断地中断它们来匮乏更长的交易.此外,随着更长时间运行的事务不断重启,这会增加CPU的负载,趋向于进一步增加这些事务的长度.最终我觉得这可能导致服务器完全没有响应.
考虑到这一点,我有以下问题:
(1)TVar(或其他数据类型)可以使用锁,而不是同时尝试/重试.
(2)可以的TVar(或其它数据类型)有一些不同的竞争机制,即"让交易运行另一个事务前一秒钟跑",或至少一些保证交易将最终完成(即竞争算法,以防止饥饿的更长时间的交易).
TVar如何运作?从我所看到的,它尝试在收到它们后立即运行所有事务,但是,事务完成使其他当前运行的事务无效,然后必须重新启动.这是TVar的工作原理吗?
如果是这种情况,如果每100ms发生1ms长的事务,这是否意味着需要200ms处理的事务永远不会完成?
通过Simon Peyton Jones并发示例,我有以下代码:
import Control.Concurrent.STM
import Control.Concurrent.STM.TVar
deposit account amount = do
bal <- readTVar account
writeTVar account (bal+amount)
Run Code Online (Sandbox Code Playgroud)
我试图在GHCi REPL中测试这个
*Main> checking <- atomically $ newTVar 100
*Main> atomically $ deposit checking 10
Run Code Online (Sandbox Code Playgroud)
如何验证我的支票余额是110美元?
我试过了
*Main> checking
*Main> readTVar checking
*Main> balance <- readTVar checking
Run Code Online (Sandbox Code Playgroud) 一个事务可以TVar以原子方式更新两个不同的s吗?即,我可以从许多TVars 组成数据结构,以减少争用?如果是这样,你能提供一个例子吗?
我是 Haskell 和 stm 的新手,我想制作一个简单的 rwlock。首先,我创建了 4 个主要函数(wlock、wunlock、rlock、runlock),需要 2 个 TVar 整数:读取线程和写入线程的数量。
此时我无法按预期使用它。我尝试这样编译
v1 <- atomically(newTVar 0);
v2 <- atomically(newTVar 0);
wlock v1 v2 -- wlock :: TVar Integer -> TVar Integer -> IO ()
Run Code Online (Sandbox Code Playgroud)
这当然很丑陋,但它有效(不知道为什么,因为原子地返回IO (TVar a)而不是TVar a)
我想要的是:
我试图通过隐藏价值观来让它变得更好。我在某处读到单子可能是可行的方法,但我还没有研究它们。相反,我尝试创建一个新类型的 Rwlock 作为
data Rwlock = Rwlock { readCant :: TVar Integer
,writeCant :: TVar Integer
}
Run Code Online (Sandbox Code Playgroud)
和一个构造函数,所以我可以做这样的事情:
import Rwlock
do{
a = rwconst;
forkIO(reader a);
forkIO(writer a);
}
Run Code Online (Sandbox Code Playgroud)
rlock a读者和作者都会打电话的地方wlock a。
问题: …