关于递增原子变量的问题?

问题描述 投票:0回答:1

见下面的代码,

AsyncTask
创建一个对等线程(定时器)来增加一个原子变量并休眠一段时间。预期的输出是打印10次
counter_
,取值范围为1到10,但实际结果很奇怪:

  • 实际结果好像是随机的,有时打印一次,有时根本不打印。
  • 此外,我发现当我将线程休眠时间(对等线程和主线程)更改为秒或毫秒时,程序按预期运行。
#include <atomic>
#include <thread>
#include <iostream>

class AtomicTest {
 public:
  int AsyncTask() {
    std::thread timer([this](){
      while (not stop_.load(std::memory_order_acquire)) {
        counter_.fetch_add(1, std::memory_order_relaxed);
        std::cout << "counter = " << counter_ << std::endl;
        std::this_thread::sleep_for(std::chrono::microseconds(1)); // both milliseconds and seconds work well
      }
    });
    timer.detach();

    std::this_thread::sleep_for(std::chrono::microseconds(10));
    stop_.store(true, std::memory_order_release);
    return 0;
  }

 private:
  std::atomic<int> counter_{0};
  std::atomic<bool> stop_{false};
};

int main(void) {
  AtomicTest test;
  test.AsyncTask();
  return 0;
}

我知道线程切换也是需要时间的,难道是线程休眠时间太短了?

我的程序运行环境:

  • Apple clang 版本 14.0.0 (clang-1400.0.29.202)
  • 目标:arm64-apple-darwin22.2.0)
c++ multithreading atomic thread-sleep stdthread
1个回答
0
投票

是的,

stop_.store
可以在新线程被调度到 CPU 内核之前或之后运行,这是很容易理解的。所以它的第一个测试将停止标志读取为
true
.

10 us 比典型的操作系统进程调度时间片(通常为 1 或 10 毫秒)短,以防相关。对于原子存储变得可见,只比内核间延迟高几个数量级。

你描述的结果正是我对像这样的依赖于时间的程序的期望,编写它是为了检测哪个线程赢得了比赛以及赢得了多少(它的速度慢

<< endl
并在写入线程内休眠。)

我绝对不希望它总是打印 10 次,而且由于线程启动开销占打印线程内 1 us 睡眠间隔的很大一部分,这种情况很少发生。

© www.soinside.com 2019 - 2024. All rights reserved.