如何在没有互斥的情况下使共享值的行为一致
How to make the shared value behave consistently without mutex?
我有以下代码。我不明白为什么读者会看到不一致的变量值。
uint64_t get_counter() {
static uint64_t counter = 0;
static std::mutex m;
std::unique_lock l(m);
return ++counter;
}
auto main() -> int {
// uint64_t shared = 0;
std::atomic<uint64_t> shared = 0;
const auto writer = [&shared]() -> void {
while (true) {
shared = get_counter();
std::this_thread::yield();
}
};
const auto reader = [&shared]() -> void {
while (true) {
const uint64_t local = shared;
if (local > shared) {
cout << local << " " << shared << endl;
}
std::this_thread::yield();
}
};
std::thread w1(writer), w2(writer), r(reader);
r.join();
return EXIT_SUCCESS;
}
get_counter
只是生成严格递增数的助手。事实上,它可以被其他更有用的功能所取代。
由于shared
永远不应该变小,我希望在评估if (local > shared)
时,它永远不应该是真的。然而,我得到的输出是这样的:
1022 1960
642677 644151
645309 645699
1510591 1512122
1592957 1593959
7964226 7965790
8918667 8919962
9094127 9095161
9116800 9117780
9214842 9215720
9539737 9541144
9737821 9739100
10222726 10223912
11197862 11199348
看起来local
确实比shared
小,但为什么输出呢?这是由某些数据竞赛引起的吗?如果是这样,如何在不引入互斥的情况下解决这个问题?std::atomic_thread_fence
可以用来提供帮助吗?shared
必须是std::atomic
吗?
在我看来,以下序列是可能的:
Writer1: get_counter() -> 2
Writer2: get_counter() -> 3
Writer2: shared = 3
Reader: local = shared (3)
Writer1: shared = 2
Reader: if (local > shared) // 3 > 2
由于你的锁不包括生成+赋值,所以你有一个";竞赛条件";在写入到共享时,它可能从一代起就出现故障,导致if被解雇。
我把竞争条件放在引号里,因为它不是数据竞争(因为共享是原子的(。
从概念上讲,从get_counter()
返回(解锁其锁(到返回值存储在shared
之间经过一段时间。在该时间间隔内,另一个线程可以完成对get_counter()
的调用,并将该(较大的(返回值存储在shared
中,可能多次。然后,第一个线程存储它最初从get_counter()
获得的值,该值较小。
例如:
Writer 1 Writer 2 Reader
Call get_counter()
get_counter() returns 5
Call get_counter()
get_counter returns 6
store 6 in shared
load 6 from shared
store 5 in shared
load 5 from shared
shared
的存储实际上应该受到与counter
相同的锁的保护,或者采取一些类似的措施来确保在可以存储计数器值之前,shared
不会被任何其他线程写入。
栅栏在这里没用。它们处理的情况是,一个线程中的加载和存储对另一个线程可见,其顺序不是第一个线程的程序顺序。在这种情况下,即使每个操作都按照它在源中出现的顺序进行,也会有一场竞赛。
相关文章:
- C++:在不中断共享的情况下通过引用传递共享 PTR?
- 在什么情况下,需要共享智能指针而无法使用唯一指针?
- 如何在多写入器情况下对文件支持的共享内存中的大页面出错
- 在这种特殊情况下,我是否需要在共享内存中使用原子类型
- 在没有共享 COM 对象的情况下使用 COM STA 或 MTA?
- 如何在不更改现有函数语法的情况下将普通指针替换为共享指针
- 在使用 boost 共享互斥体时,我应该在什么情况下使用 owns_lock() 函数
- 在不违反严格的别名规则的情况下访问进程间共享内存中的对象
- 一种在没有 root 的情况下加载共享库的更简单方法
- C 共享_ptr使用情况下是否会有任何泄漏
- 关于在这种情况下消息队列与共享内存的适用性或适用性
- RAII 是否可以在不同步的情况下有效地在线程之间共享不可变对象
- 在不增加引用计数的情况下迭代共享指针的映射
- 是否可以使用 std::shared_ptr 创建共享对象池,并在没有自定义析构函数的情况下创建weak_ptr
- 如何*首先*获得独占锁,然后在不释放锁的情况下降级为共享锁
- 这是存储 std::分配器状态的正确方法 - 在这种情况下,由 Windows 上的共享内存支持
- 在不释放内存的情况下调用共享库
- C++:在共享所有权的情况下,C++11移动语义可以避免指针吗
- 共享指针在未分配的情况下工作
- 在GCC中,如何在不导出所有符号的情况下导出共享库的所有typeinfo符号