我正在尝试使用 C++11 原子原语来实现某种原子“线程计数器”。基本上,我有一个关键的代码部分。在此代码块中,任何线程都可以从内存中自由读取。但是,有时,我想做一个重置或清除操作,将所有共享内存重置为默认的初始化值。
这似乎是使用读写锁的绝佳机会。C++11 不包括开箱即用的读写互斥锁,但也许更简单的东西会做。我认为这个问题将是一个更好地熟悉 C++11 原子原语的好机会。
所以我想了一会儿这个问题,在我看来,我所要做的就是:
每当线程进入临界区时,增加一个原子计数器变量
每当线程离开临界区时,递减原子计数器变量
如果一个线程希望将所有变量重置为默认值,它必须自动等待计数器为 0,然后自动将其设置为某个特殊的“清除标志”值,执行清除,然后将计数器重置为 0。
当然,希望增加和减少计数器的线程还必须检查清除标志。
所以,我刚才描述的算法可以用三个函数来实现。第一个函数,increment_thread_counter()
必须始终在进入临界区之前调用。第二个函数,decrement_thread_counter()
,必须总是在离开临界区之前被调用。最后,只有当线程计数器 == 0时,clear()
才能从临界区外部调用该函数。
这就是我想出的:
鉴于:
- 线程计数器变量,
std::atomic<std::size_t> thread_counter
- 一个常数
clearing_flag
设置为std::numeric_limits<std::size_t>::max()
...
void increment_thread_counter()
{
std::size_t expected = 0;
while (!std::atomic_compare_exchange_strong(&thread_counter, &expected, 1))
{
if (expected != clearing_flag)
{
thread_counter.fetch_add(1);
break;
}
expected = 0;
}
}
void decrement_thread_counter()
{
thread_counter.fetch_sub(1);
}
void clear()
{
std::size_t expected = 0;
while (!thread_counter.compare_exchange_strong(expected, clearing_flag)) expected = 0;
/* PERFORM WRITES WHICH WRITE TO ALL SHARED VARIABLES */
thread_counter.store(0);
}
据我所知,这应该是线程安全的。请注意,该decrement_thread_counter
函数不应该需要任何同步逻辑,因为它是一个increment()
总是在之前调用的给定decrement()
。因此,当我们到达 时decrement()
,thread_counter 永远不会等于 0 或clearing_flag
。
无论如何,由于 THREADING IS HARD™,而且我不是无锁算法方面的专家,我不完全确定该算法是否无竞争条件。
问题:这个代码线程安全吗?这里有任何可能的比赛条件吗?