【问题标题】:Trying to understand Haskell STM simple things试图理解 Haskell STM 简单的东西
【发布时间】:2011-02-02 08:56:52
【问题描述】:

我一直在理解 STM 中的原子概念。

我举例说明

import Control.Concurrent
import Control.Concurrent.STM
import Control.Monad
import qualified Data.Map as Map 

main :: IO ()
main =  do
    d <- atomically$ newTVar Map.empty
    sockHandler  d 

sockHandler ::  TVar (Map.Map String Int)-> IO ()
sockHandler  d = do
    forkIO $ commandProcessor  d 1
    forkIO $ commandProcessor  d 2
    forkIO $ commandProcessor  d 3
    forkIO $ commandProcessor  d 4
    forkIO (threadDelay 1000 >> putStrLn "Hello World?")

    threadDelay 10000
    return ()

commandProcessor ::  TVar (Map.Map String Int)-> Int-> IO ()
commandProcessor  d i= do
  addCommand d i
  commandProcessor  d i 

addCommand  ::  TVar (Map.Map String Int) ->Int -> IO ()
addCommand    d i = do
  succ <- atomically $ runAdd d
  putStrLn  $"Result of add in " ++ (show i)++ " " ++( show succ)

runAdd  d =do
  dl <- readTVar d
  let (succ,g)= if   Map.member "a" dl
                  then
                      (False,dl)
                  else
                      (True,Map.insert "a" 9 dl)
  writeTVar d g
  return succ

示例输出如下:

添加结果 1 True 添加结果 in 4 False add in 1 的结果 FalseResult of add in 2 FalseResult of 添加 3 False Hello World?的结果 加4个假

添加结果 1 FalseResult of add in 2 False add in 3 False 的结果 添加结果 4 False

添加结果 1 False 添加结果 in 2 FalseResult of add in 3 False 加 4 的结果 False

添加结果 1 False 添加结果 in 2 FalseResult of add in 3 False 加 4 的结果 False

添加结果 1 False 添加结果 in 2 FalseResult of add in 4 False 加 3 的结果 False

添加结果 1 False 添加结果 in 4 FalseResult of add in 2 False 加 3 的结果 False

添加结果 1 FalseResult of add in 4 False add in 2 False 的结果 加 3 False 的结果

添加结果 1 FalseResult of add 在 4 错误

添加的结果 2 FalseResult of add 在 3 错误

添加结果 1 FalseResult of add 在 4 错误

添加的结果 2 FalseResult of add in 3 False add in 1 False 的结果 添加结果 4 False

添加的结果 2 FalseResult of add 在 3 错误

添加结果 1 FalseResult of add 在 4 假

当我读到关于原子的

。这意味着事务中的所有操作完全完成,没有任何其他线程修改我们的事务正在使用的变量,或者它失败,并且状态回滚到事务开始之前的位置。简而言之,原子事务要么完全完成,要么就好像它们从未运行过一样。

那么对于这个问题,在某些情况下,成功的“回归”可能永远不会发生吗? 那是行 succ

给出“添加结果?i”的输出(“好像它们根本没有运行”)

【问题讨论】:

    标签: haskell ghc transactional-memory


    【解决方案1】:

    如果事务确实回滚,那么您的程序会再次尝试。你可以想象atomically的实现是这样的:

    atomically action = do varState <- getStateOfTVars
                           (newState, ret) <- runTransactionWith action varState
                           success <- attemptToCommitChangesToTVars newState
                           if success
                             then return ret
                             else atomically action -- try again
    

    在您的情况下,事务将始终运行并始终完成。由于冲突,它可能会在第二次或第三次尝试时完成,但这对你来说是不可见的,用户。 STM 确保动作以原子方式发生,即使它需要几次才能成功完成。

    【讨论】:

    • 很好,你有这方面的参考吗?
    • 我发现原来的 Haskell-STM 论文可读性很强,你可以在这里得到一份副本:research.microsoft.com/en-us/um/people/simonpj/papers/stm/… 根据你的例子,我认为只阅读第 3 节和第 6 节对你来说就足够了,应该让您对 STM 的工作原理有一个很好的了解。
    • 注意,如果你需要重试直到成功以外的行为,你可以使用Control.Monad.STM中的函数来实现它。
    【解决方案2】:
    1. threadDelay 已经返回 (),之后不需要显式地 return ()
    2. newTVarIOatomically . newTVar 的简洁版本。
    3. 如果您使用forever 而不是像commandProcessor 中所做的那样使用tail 调用自己,那么它会更具可读性。

    至于你的问题,答案是“是”。它被称为活锁,您的线程有工作要做,但它无法取得进展。想象一个非常昂贵的函数 expensive 和一个非常便宜的函数 cheap。如果它们在同一个TVar 上的竞争atomically 块中运行,那么廉价函数可能会导致expensive 函数永远无法完成。我为related SO question 构建了一个示例。

    不过,您的结束示例并不完全正确,如果 STM 操作从未​​完成,则将永远无法到达 putStrLn,并且该线程根本看不到任何输出。

    【讨论】:

    • 技术上的饥饿是线程有工作要做但无法取得进展的情况。活锁是没有线程取得进展的情况(由于它们的活动反复导致 all 中止),并且在具有惰性获取语义的 STM 系统中极不可能发生。
    • @ben 我习惯于将starvation 定义为线程可以工作但从未运行,通常是由于调度的某些方面。
    • 所以,我可以得到一个活锁,但 launchMissile (putStrnLn) 永远不会发射。我更害怕我可能会得到一个活锁并且最终会超时并让我处于随机值的启动状态。
    • @TomMD 我更喜欢将饥饿与活锁和死锁分开。饥饿是指有限的资源(对锁的访问、完成事务的足够长的非竞争窗口等)被不公平地分配给线程,使得其中一些线程无法取得进展,即使系统的吞吐量可能很好。关键是,给定有限的工作量,饥饿的进程最终仍会完成。
    • @TomMD OTOH 活锁,就像死锁一样,涉及线程相互干扰,以至于它们都无法取得进展。虽然如果有其他非锁定线程正在取得进展,吞吐量可能仍然很好,但即使工作负载有限,该进程也永远不会完成。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多