LoginSignup
5
5

More than 5 years have passed since last update.

std::atomicを使う際の注意点の一つ

Last updated at Posted at 2018-10-06

はじめに

私は先日TeraTailに質問を投稿して、有用そうな情報を得たので一応投稿しておきます。

std::atomicを使う際の注意点の一つ

std::atomicに間接参照用のポインタを入れても、参照を解決するのはスレッドセーフになりませんよ!

以下のコードを見てください。

#include <iostream>
#include <atomic>
#include <thread>

struct mystruct
{
    ~mystruct() { value = 0; }
    int value = 0;
};

class myclass
{
    mystruct m_mystruct;
    std::atomic<mystruct*> m_atomic_mystruct_ptr;
public:
    myclass() { m_atomic_mystruct_ptr.store(&m_mystruct); }
    mystruct & get_mystruct() { return *m_atomic_mystruct_ptr.load(); }
    void mystruct_worker(std::size_t num) { 
        while(num --> 0)
            m_atomic_mystruct_ptr.load()->value++; 
    }
};

int main()
{
    myclass m;
    std::thread t(&myclass::mystruct_worker, &m, 10000);
    std::this_thread::sleep_for(std::chrono::nanoseconds(100));
    int i = m.get_mystruct().value;  // ← 未定義動作!!
    std::cout << i << "\n";
    t.join();
}

このように、std::atomicにポインタを入れて使用しようとしたとします。しかし、スレッドセーフにアクセスできるのはポインタの値を読み出す処理だけ、つまりメモリのアドレスを取得する処理だけで、*間接参照演算子を使った代入などの、そのアドレスのさす先の値を変更したり読み出したりする処理はスレッドセーフになっていないです。

もちろん、std::atomic<char*>のようにして"文字列"をスレッドセーフに扱うのも未定義です。std::atomicに格納しているのは、あくまでchar型のポインタ(=メモリアドレス)です。
std::atomicに入れたいクラスのメンバに、間接参照用のポインタが存在するかもしれないことにも注意が必要かもですよ!

余談

おそらく、このような処理をしたい方は、

  • std::atomicにはトリビアルコピー可能な型しか入れられないので、何とかポインタで代用できないかと思った
  • std::mutexを使った実装は、スレッドセーフにしたい処理の全部の最初に実装するのが正直大変だし、速度も遅くなりそう

などの理由があったと思います。

そんな方には、boost::synchronized_value<T>があります。
内部的にmutexを使用しているので、速度に関してはmutexと同じくらいになりますが、こちらは特に型に制約はなく、std::atomicと同じような文脈で使えます。
競合も何もない簡単なベンチマークを行ってみたところ、処理時間は、std::atomic < std::mutexboost::synchronized_value<T>のようになりました。コードは、下にリンクを貼っておきます。

参照

boost::synchronized_value<T>の使い方
簡単な速度のベンチマーク ← エラーの山は無視してください。コンパイルは通るのですが......
Teratailに投稿した質問

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5