尝试理解 Haskell STM 简单的事情

发布于 2024-10-15 14:58:15 字数 2489 浏览 8 评论 0原文

我陷入了对 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 添加 in 1 的结果 FalseResult 添加 2 FalseResult 添加 3 个 False Hello World?结果 添加 4 个错误

add 的结果 1 个 FalseResult of add in 2 False 添加结果 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 添加到 2 的 FalseResult False 添加 3 False 结果

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

add 的结果 1 个 FalseResult of add 4 错误

add的结果 2 FalseResult of add 3 错误

add 的结果 1 个 FalseResult of add 4 错误

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

add的结果 2 FalseResult of add 3 错误

add 的结果 1 个 FalseResult of add 4 错误

当我读到atomically

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

那么问题来了,在某些情况下,succ 的“返回”是否永远不会发生? 那就是可以的线 succ <- 原子地 $ runAdd d putStrLn $"Add in " ++ (show i)++ " " ++( show succ)

给出的输出为 "Result of add in ?i " ("就好像它们根本没有运行过一样")

I got stuck in understanding the concept of atomically in STM.

I illustrate with an example

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

The sample output would be like this:

Result of add in 1 True Result of add
in 4 False Result of add in 1
FalseResult of add in 2 FalseResult of
add in 3 False Hello World? Result of
add in 4 False

Result of add in 1 FalseResult of add
in 2 False Result of add in 3 False
Result of add in 4 False

Result of add in 1 False Result of add
in 2 FalseResult of add in 3
FalseResult of add in 4 False

Result of add in 1 False Result of add
in 2 FalseResult of add in 3
FalseResult of add in 4 False

Result of add in 1 False Result of add
in 2 FalseResult of add in 4
FalseResult of add in 3 False

Result of add in 1 False Result of add
in 4 FalseResult of add in 2
FalseResult of add in 3 False

Result of add in 1 FalseResult of add
in 4 False Result of add in 2 False
Result of add in 3 False

Result of add in 1 FalseResult of add
in 4 False

Result of add in 2 FalseResult of add
in 3 False

Result of add in 1 FalseResult of add
in 4 False

Result of add in 2 FalseResult of add
in 3 False Result of add in 1 False
Result of add in 4 False

Result of add in 2 FalseResult of add
in 3 False

Result of add in 1 FalseResult of add
in 4 False

When I read about atomically

. This means that all operations inside the transaction fully complete, without any other threads modifying the variables that our transaction is using, or it fails, and the state is rolled back to where it was before the transaction was begun. In short, atomic transactions either complete fully, or it is as if they were never run at all.

So to the question could the "return" of succ in some cases never happen?
That is could the line
succ <- atomically $ runAdd d
putStrLn $"Result of add in " ++ (show i)++ " " ++( show succ)

give an output of "Result of add in ?i " ("as if they were never run at all")

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

桃酥萝莉 2024-10-22 14:58:15

如果事务确实回滚,您的程序会再次尝试。您可以将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 确保操作以原子方式发生,即使需要经过几次操作才能成功完成。

If a transaction does get rolled back, what happens is that your program tries again. You can imagine the implementation of atomically to be something like this:

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

In your case, the transaction will always run and will always complete. It may complete on the second or third try due to conflicts, but that's invisible to you, the user. STM makes sure that the action happens atomically, even if it takes a few goes before it's able to do that successfully.

情泪▽动烟 2024-10-22 14:58:15
  1. threadDelay 已经 returns (),之后不需要显式 return ()
  2. newTVarIOatomically 的简洁版本。新TVar。
  3. 如果您使用 forever 而不是像 commandProcessor 中那样尾部调用自己,那么它的可读性会更高。

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

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

  1. threadDelay already returns (), no need to explicitly return () afterward
  2. newTVarIO is a concise version of atomically . newTVar.
  3. It's more readable if you use forever instead of tail calling yourself as done in commandProcessor.

As for your question, the answer is "yes". It's called live-lock in which your thread has work to do but it can't make progress. Imagine a really expensive function, expensive, and a really cheap function, cheap. If these operate in competing atomically blocks on the same TVar then the cheap function can cause the expensive function to never complete. I built an example for a related SO question.

Your closing example is not quite right though, if the STM operation never completes then putStrLn will never be reached and no output will be seen at all from that thread.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文