You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Data.Atomics.Counter统计不同线程随机操作的执行顺序

问题根因

原来的代码出现重复计数器值的核心原因是readIORef和modifyIORef是两个独立的非原子操作,多个线程可以同时读到同一个计数器值,之后各自修改写回,就会出现计数重复、跳号的问题。

Data.Atomics.Counter 核心用法

这个库提供了硬件级别的原子计数器操作,不需要手动加锁就能保证多线程下操作的安全性,核心常用操作如下:

  • newCounter :: Int -> IO Counter:初始化计数器,传入初始值
  • incrCounter :: Int -> Counter -> IO Int:原子地给计数器增加指定数值,返回增加前的旧值
  • readCounter :: Counter -> IO Int:原子读取计数器的当前值

你需求的「先拿到当前计数、再自增」的逻辑刚好可以通过incrCounter 1一步完成,整个操作是原子的,不会被其他线程打断,完全避免并发冲突。

修复后的完整示例代码

首先需要在项目依赖中添加atomic-primops库,之后使用以下代码:

import Control.Concurrent (forkIO)
import Control.Monad (forever, void)
import Data.Atomics.Counter (Counter, newCounter, incrCounter)

main :: IO ()
main = do
  -- 初始化计数器,初始值设为1
  myCounter <- newCounter 1
  -- 第一个子线程
  void . forkIO . forever $ do
    curSeq <- incrCounter 1 myCounter
    print ("hi " ++ show curSeq)
  -- 第二个子线程
  void . forkIO . forever $ do
    curSeq <- incrCounter 1 myCounter
    print ("hey " ++ show curSeq)
  -- 主线程循环
  forever $ do
    curSeq <- incrCounter 1 myCounter
    print ("hello " ++ show curSeq)
可选优化:保证打印顺序与序号严格一致

上述实现中,计数器的序号是取号的顺序,由于操作系统对不同线程的标准输出调度存在不确定性,可能出现序号小的内容后打印的情况。如果需要严格保证控制台输出顺序和序号一致,可以加MVar锁将取号+打印的操作串行化:

import Control.Concurrent (forkIO, MVar, newMVar, takeMVar, putMVar)
import Control.Monad (forever, void)
import Data.Atomics.Counter (Counter, newCounter, incrCounter)

main :: IO ()
main = do
  myCounter <- newCounter 1
  -- 初始化打印锁
  printLock <- newMVar ()
  let lockedPrint msg = takeMVar printLock >> print msg >> putMVar printLock ()
  void . forkIO . forever $ do
    curSeq <- incrCounter 1 myCounter
    lockedPrint ("hi " ++ show curSeq)
  void . forkIO . forever $ do
    curSeq <- incrCounter 1 myCounter
    lockedPrint ("hey " ++ show curSeq)
  forever $ do
    curSeq <- incrCounter 1 myCounter
    lockedPrint ("hello " ++ show curSeq)

内容的提问来源于stack exchange,提问作者Anon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 16:27:03