49

的定义std::atomic<>似乎显示了它对原始类型或可能是 POD 类型的明显有用性。

你什么时候会用它来上课?

什么时候应该避免在课堂上使用它?

4

4 回答 4

32

std::atomic在任何可简单复制的类型上可用的操作都是非常基本的。可以构造和销毁atomic<T>,可以询问类型is_lock_free(),可以加载和存储 的副本T,还可以通过各种方式交换 的值T。如果这足以满足您的目的,那么您可能比持有显式锁定更好。

如果这些操作还不够,例如,如果您需要直接在值上原子地执行序列操作,或者如果对象足够大以至于复制成本很高,那么您可能希望持有一个您管理的显式锁实现您更复杂的目标或避免做所有使用atomic<T>会涉及的副本。

// non-POD type that maintains an invariant a==b without any care for
// thread safety.
struct T { int b; }
struct S : private T {
    S(int n) : a{n}, b{n} {}
    void increment() { a++; b++; }
private:
    int a;
};

std::atomic<S> a{{5}}; // global variable

// how a thread might update the global variable without losing any
// other thread's updates.
S s = a.load();
S new_s;
do {
    new_s = s;
    new_s.increment(); // whatever modifications you want
} while (!a.compare_exchange_strong(s, new_s));

如您所见,这基本上是获取值的副本,修改副本,然后尝试将修改后的值复制回来,必要时重复。您对副本所做的修改可以随心所欲地复杂,而不仅仅限于单个成员函数。

于 2012-12-14T21:22:04.490 回答
14

它适用于原始类型和 POD 类型。类型必须是memcpy-able,所以更通用的类已经出来了。

于 2012-12-14T20:17:04.467 回答
7

标准说

原子模板的特化和实例化应具有已删除的复制构造函数、已删除的复制赋值运算符和 constexpr 值构造函数。

如果这与 Pete Becker 的答案完全相同,我不确定。我对此进行了解释,以便您可以自由地专注于自己的课程(不仅是 memcpy-able 课程)。

于 2012-12-14T20:34:21.067 回答
2

对于这种情况,我更喜欢 std::mutex 。尽管如此,我还是尝试了一个糟糕的 mans 基准测试来在单线程(因此完美同步)环境中使用 std::atomics 和 std::mutex 来分析一个版本。

#include <chrono>
#include <atomic>
#include <mutex>

std::mutex _mux;
int i = 0;
int j = 0;
void a() {
    std::lock_guard<std::mutex> lock(_mux);
    i++;
    j++;
}

struct S {
    int k = 0;
    int l = 0;

    void doSomething() {
        k++;
        l++;
    }
};

std::atomic<S> s;
void b() {
    S tmp = s.load();
    S new_s;
    do {
        new_s = tmp;
        //new_s.doSomething(); // whatever modifications you want
        new_s.k++;
        new_s.l++;
    } while (!s.compare_exchange_strong(tmp, new_s));
}

void main(void) {

    std::chrono::high_resolution_clock clock;

    auto t1 = clock.now();
    for (int cnt = 0; cnt < 1000000; cnt++)
        a();
    auto diff1 = clock.now() - t1;

    auto t2 = clock.now();
    for (int cnt = 0; cnt < 1000000; cnt++)
        b();
    auto diff2 = clock.now() - t2;

    auto total = diff1.count() + diff2.count();
    auto frac1 = (double)diff1.count() / total;
    auto frac2 = (double)diff2.count() / total;
}

在我的系统上,使用 std::mutex 的版本比 std::atomic 方法快。我认为这是由值的额外复制引起的。此外,如果在多线程环境中使用,繁忙的循环也会影响性能。

总而言之,是的,可以将 std::atomic 与各种 pod 类型一起使用,但在大多数情况下,std::mutex 是首选武器,因为它有意更容易理解正在发生的事情,因此不那么容易与 std::atomic 一起呈现的版本中的错误。

于 2015-11-03T14:32:23.327 回答