簡體   English   中英

如何有效地將std :: atomic <>用於非原始類型?

[英]How to use std::atomic<> effectively for non-primitive types?

std::atomic<>的定義似乎表明它對原始或POD類型有明顯的用處。

你什么時候才能將它用於課堂?

什么時候應該避免將它用於課堂?

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));

如您所見,這基本上獲取值的副本,修改副本,然后嘗試復制修改后的值,並根據需要重復。 您對副本所做的修改可能非常復雜,而不僅限於單個成員函數。

它適用於原始和POD類型。 類型必須是memcpy -able,因此更普遍的類已經出來了。

標准說

原子模板的特化和實例化應具有已刪除的復制構造函數,已刪除的復制賦值運算符和constexpr值構造函數。

如果這與Pete Becker的答案完全相同,我不確定。 我解釋這一點,你可以自由地專注於你自己的課程(不僅僅是memcpy-able課程)。

對於這種情況,我更喜歡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一起提供的版本的bug。

使用Visual Studio 2017時,由於嘗試將std :: atomic與類一起使用時出現“對齊”問題,我遇到了編譯器錯誤C2338; 你使用std :: mutex要好得多,我最后還是做了。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM