4

我正在尝试使用 C++11 原子原语来实现某种原子“线程计数器”。基本上,我有一个关键的代码部分。在此代码块中,任何线程都可以从内存中自由读取。但是,有时,我想做一个重置或清除操作,将所有共享内存重置为默认的初始化值。

这似乎是使用读写锁的绝佳机会。C++11 不包括开箱即用的读写互斥锁,但也许更简单的东西会做。我认为这个问题将是一个更好地熟悉 C++11 原子原语的好机会。

所以我想了一会儿这个问题,在我看来,我所要做的就是:

  1. 每当线程进入临界区时,增加一个原子计数器变量

  2. 每当线程离开临界区时,递减原子计数器变量

  3. 如果一个线程希望将所有变量重置为默认值,它必须自动等待计数器为 0,然后自动将其设置为某个特殊的“清除标志”值,执行清除,然后将计数器重置为 0。

  4. 当然,希望增加和减少计数器的线程还必须检查清除标志。

所以,我刚才描述的算法可以用三个函数来实现。第一个函数,increment_thread_counter()必须始终在进入临界区之前调用。第二个函数,decrement_thread_counter(),必须总是在离开临界区之前被调用。最后,只有当线程计数器 == 0时,clear()才能从临界区外部调用该函数。

这就是我想出的:

鉴于:

  1. 线程计数器变量,std::atomic<std::size_t> thread_counter
  2. 一个常数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™,而且我不是无锁算法方面的专家,我不完全确定该算法是否无竞争条件。

问题:这个代码线程安全吗?这里有任何可能的比赛条件吗?

4

1 回答 1

6

你有一个竞争条件;如果另一个线程改变了increment_thread_counter()'s test forclearing_flagfetch_add.

我认为这个经典的 CAS 循环应该更好用:

void increment_thread_counter()
{
    std::size_t expected = 0;
    std::size_t updated;
    do {
        if (expected == clearing_flag) {     // don't want to succeed while clearing, 
             expected = 0;      //take a chance that clearing completes before CMPEXC
        }

        updated = expected + 1;
        // if (updated == clearing_flag) TOO MANY READERS!
    } while (!std::atomic_compare_exchange_weak(&thread_counter, &expected, updated));
}
于 2014-08-07T17:53:32.607 回答