Clojure STM(dosync)方法和Java同步Block有什么区别?
我正在阅读"睡觉的理发师"问题下面的代码.(http://www.bestinclass.dk/index.clj/2009/09/scala-vs-clojure-round-2-concurrency.html)
(defn the-shop [a]
(print "[k] entering shop" a)
(dosync
(if (< (count @queue) seats)
(alter queue conj a)
(print "[s] turning away customer" a))))
Run Code Online (Sandbox Code Playgroud)
为了避免竞争条件,dosync使用,所以我问自己"Java同步块有什么区别(STM)"?它会阻止这个关键代码吗?
提前致谢 !丹塔斯
我有一种情况需要在不同的机器上编译一些Haskell代码.这些机器中至少有一台有一个相当旧版本的Control.Concurrent.STM,它不知道modifyTVar.我目前的解决方法是从较新版本的软件包中复制modifyTVar的代码.这让我想知道,是否可以使用模板Haskell来检查函数是否已经定义并且只定义它,如果它丢失了.我知道正确的解决方案可能是获得更新的软件包,但这种情况让我很好奇.
haskell的stm库中有一个带有以下类型签名的函数:
alwaysSucceeds :: STM a -> STM ()
Run Code Online (Sandbox Code Playgroud)
根据我对haskell中STM的理解,有三种方法可以在执行STM计算时"出错"(使用该术语):
retry重启来触发的.这有效地使线程阻塞,然后一旦读取集中的TVar改变就重试.throwSTM会导致这种情况.这个与前两个不同,因为事务没有重新启动.相反,错误会传播并导致程序崩溃或被IO monad捕获.如果这些是准确的(如果不是,请告诉我),我无法理解alwaysSucceeds可能做什么.该always函数似乎构建在它之上,看起来它可以写成没有alwaysSucceeds:
--This is probably wrong
always :: STM Bool -> STM ()
always stmBool = stmBool >>= check
Run Code Online (Sandbox Code Playgroud)
文档alwaysSucceeds说:
alwaysSucceeds添加了一个新的不变量,当传递给alwaysSucceeds时,在当前事务结束时以及每个后续事务结束时都必须为true.如果它在任何这些点失败,那么违反它的事务将被中止,并且传播由不变量引发的异常.
但由于参数是类型STM a(多态a),因此它不能使用事务为决策制定的任何部分返回的值.所以,似乎它会寻找我之前列出的不同类型的故障.但那有什么意义呢?STM monad已经处理了故障.如何将它包装在此函数中会影响它?为什么类型的变量会a被丢弃,导致STM ()?
我是Scala和Akka的新手,请原谅我,如果这是一个新问题,但我无法在其他任何地方找到答案......
为了记录我使用Scala 2.9.0-1和Akka 1.1.3并且还包括我的SBT 0.10.1设置.
我在Akka上编写了这条消息后面的代码作为实验; 它是用户数据库和注册设施的玩具版本.基本的想法是有一个UserPool的ActorPool,每个actor都有一个MemoryUserDatabase的实例,它使用STM与用户的电子邮件地址键入的用户地图进行交互 - 非常简单,对吧?
可以通过编译文件并在两个单独的控制台中运行以下内容来重现此问题:
控制台#1:
import toy.service.user._; ServiceRunner.run
控制台#2:
import toy.service.user._; ClientRunner.run
这是服务器控制台的输出(#1)
Aug 31, 2011 5:21:29 PM org.multiverse.api.GlobalStmInstance <clinit>
INFO: Initializing GlobalStmInstance using factoryMethod 'org.multiverse.stms.alpha.AlphaStm.createFast'.
Aug 31, 2011 5:21:29 PM org.multiverse.stms.alpha.AlphaStm <init>
INFO: Created a new AlphaStm instance
Aug 31, 2011 5:21:29 PM org.multiverse.api.GlobalStmInstance <clinit>
INFO: Successfully initialized GlobalStmInstance using factoryMethod 'org.multiverse.stms.alpha.AlphaStm.createFast'.
[ERROR] [8/31/11 5:21 PM] [akka:event-driven:dispatcher:global-3] [LocalActorRef] Availability(foo)
java.lang.NoClassDefFoundError: Could not initialize class toy.service.user.memory.MemoryUserDatabase$
at toy.service.user.memory.MemoryUserDatabase$$anonfun$getUser$1.apply(Registration.scala:96)
at toy.service.user.memory.MemoryUserDatabase$$anonfun$getUser$1.apply(Registration.scala:96)
at toy.service.user.memory.MemoryUserDatabase$$anonfun$getUser$2.apply(Registration.scala:96)
at toy.service.user.memory.MemoryUserDatabase$$anonfun$getUser$2.apply(Registration.scala:96) …Run Code Online (Sandbox Code Playgroud) 有没有办法增加一个时间间隔,在此基础上RTS决定该线程在STM事务中无限期地被阻塞?这是我的代码:
import Control.Concurrent (ThreadId)
import Control.Concurrent.MVar (MVar,newMVar,withMVar)
import Control.Concurrent.STM
import qualified Control.Concurrent.ThreadManager as TM
data ThreadManager = ThreadManager { tmCounter::TVar Int, tmTM::MVar TM.ThreadManager }
data Settings = Settings {
maxThreadsCount::Int }
createThreadManager :: Settings -> IO ThreadManager
createThreadManager s = do
counter <- atomically $ newTVar (maxThreadsCount s)
tm <- TM.make >>= newMVar
return $ ThreadManager counter tm
forkManaged :: ThreadManager -> IO () -> IO ThreadId
forkManaged tm fn = do
atomically $ do
counter <- readTVar $ tmCounter …Run Code Online (Sandbox Code Playgroud) 我是STM的新手.我想在Haskell中做的一件事涉及大量数据,以及大量轻量级线程读取和写入所述大数据的一小部分.读取和写入的位置可以被认为是基本上随机的和小的.STM看起来很棒,但我对如何解决这个问题有一些疑问.我看到了几种可能的方法,每种方法都有一些缺点,有些方法看起来很愚蠢.关于这些或其他替代方案的一些评论将不胜感激.
让我们简单地假设大块数据是a Data.Vector a,其中元素本身很小.
整块数据为一体TVar (Vector a).我想这将导致大量数据的复制,因为STM会认为每个单独的写入可能会影响整个共享数据.当然,STM确定读写是非常本地化的,并且不需要跨大数据的一致性,这是不可思议的?
大量的TVar as,基本上每个元素一个,给出完全本地化的STM,但基本上复制了整个Vector a.这看起来很愚蠢.
通过对数据进行分段以使得我具有TVar (Vector a)与数据的子向量对应的合理数量的s来在1和2之间进行折衷.我觉得这个解决方案太依赖于启发式,比如细分应该有多大.
消息.不是每个工作者使用STM读取和写入数据,而是每个都写入带有要读取数据的请求的消息或者要通过某些STM机制写入的数据块,例如a TChan.特殊线程接收这些消息,传递通过另一个消息请求的数据TChan,或者接收数据并将其写入共享数据结构.这个解决方案似乎没有困扰解决方案1-3的问题,但在我看来,它基本上放弃了使用STM的细节来保持数据的一致性.相反,它只是消息传递.当然,消息传递部分是用STM实现的,但我的真正问题是通过消息传递解决的.STM似乎很棒,消息传递是如此......嗯.
我正确地考虑了这个吗?有人有任何提示或其他建议吗?
请记住,我没有使用STM的经验,也没有尝试过上述解决方案.我会离开扶手椅,但有时在尝试任何事情之前考虑这些事情会很好.
附录:第五种方法来自Nathan Howell并使用TArray.这听起来像我想要的,但文档说:
它目前被实现为Array ix(TVar e),但它可能在将来被更有效的实现取代(但是接口将保持不变).
我认为这意味着这TArray只是我穿着更好衣服的第2号方法.暗示"更有效"实施的文档很有意思,因为它暗示实际上有一种更好的方法.
Vagif Verdi的答案非常有趣,所以我做了一个小实验来试试.我现在没有时间减少代码,所以对此感兴趣的人将不得不承担代码而不仅仅包含基本要素.我决定使用一个10 ^ 8 Ints 的可变向量作为"大共享数据",让多个读者/写者对应于进入网络套接字的线程.
请注意,代码甚至不会读取或写入共享数据.它只是在那里,每个线程都拥有TVar它.
那会发生什么?我运行程序,并立即占用大约780 MB的RAM,这是预期的(它大致是10 ^ 8 Int需要的).现在,如果我使用netcat连接几个客户端并编写一些文本,程序应该打印出来,甚至不写入共享数据,那么进程的CPU使用率会高达100%,持续时间超过一秒!在显示文本之前有明显的延迟.从好的方面来看,内存使用率保持不变(根据Vagif Verdi的回答).如果我删除了矢量TVar,即取出所有STM和共享数据,一切都非常快速和响应,并且每当客户端写入内容时,没有明显的CPU使用率. …
首先,一些背景.我想要一个队列,我想以两种不同的模式之一操作.在第一种模式中,我希望能够检索队列中存在的元素,但是如果没有元素则不能阻止.在第二种模式中,我希望能够阻塞,直到队列中有一个元素.(我知道我可以为每种模式使用专门的机制,但我想分解一些常用代码,因此如果我可以对两种操作模式使用相同的机制,那将是最简单的.)
我可以使用Chan,但根据我不应该使用的文档,isEmptyChan因为它可能会因为死锁而被弃用.这让我失望了TChan.该tryReadTChan函数为我提供了我想要的第一种模式(即我可以检查元素是否存在而没有阻塞),但我不确定究竟是什么readTChan.我的心智模型是该atomically块将继续重试,直到通道中存在一个元素,这意味着它将忙于循环浪费CPU周期; 这是不同的readChan(即,非STM版本)(如果我理解正确的话)将实际阻止线程的执行,直到元素可用,因为运行时线程调度程序理解MVars.
那么,TChan就像Chan在,如果我使用readTChan了运行时是足够聪明,直到值可用不安排调用线程?或者它会浪费大量的CPU周期不断轮询到一个值到达?
免责声明:这可以使用MVar ()简单的互斥锁轻松完成.我只是想知道它是否可以用STM完成.
我想原子地做以下事情:
阅读一些变量.
根据我刚刚阅读的内容决定要执行的I/O.
执行I/O.
将结果记录在变量中.
具体来说,假设我想跟踪我已读取的输入字节数,并假设在消耗了一定数量的字节后达到了EOF.(好吧,让两个线程同时从同一个文件中读取,可能是首先要做的事情,但请跟我一起去...)
显然,这不能是单个STM交易; 中间是I/O. 显然,将它作为两个未连接的交易也是错误的.(两个线程可以看到剩下一个字节的配额,并且都决定读取该字节.)
这个问题有一个很好的解决方案吗?或者STM只是这个任务的错误工具?
有没有一个很好的理由为什么库check中的Contol.Concurent.STM函数具有类型Bool -> STM a并且返回undefined成功而不是具有类型Bool -> STM ()?它实现的方式是类型检查器将polity编译一个do块结束,check foo只是在运行时失败*** Exception: Prelude.undefined.