58
44

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

C++ メモリバリア 使い方

Last updated at Posted at 2017-10-31

必要性に追われて、メモリバリアを使ったコードを書くことになったので、自分用のメモとして残しておいたものです。

#メモリバリア

MemoryBarrier(メモリバリア)または MemoryFence(メモリフェンス)とは、その前後のメモリ操作の順序性を制限するCPUの命令の一種である。

C++の標準ライブラリにおける、メモリバリアの定義はこちら。

namespace std {
  enum memory_order {
    memory_order_relaxed,
    memory_order_consume,
    memory_order_acquire,
    memory_order_release,
    memory_order_acq_rel,
    memory_order_seq_cst
  };
}

※この記事では、memory_order_acquireと、memory_order_releaseだけを扱っています。
#マルチスレッドプログラムにおける利用例

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

int data;
std::atomic<bool> ready(false);

void f()
{
  while (!ready.load(std::memory_order_acquire)) {
  }

  std::cout << data << std::endl;
}

int main()
{
  std::thread t(f);

  data = 765;
  ready.store(true, std::memory_order_release);

  t.join();
}

##アトミック変数
std::atomic

アトミック変数とは、不可分な読み出しや書き込み、および読み書きを同時に行える変数である。

アトミック変数への操作は、他のスレッドによって割り込まれることがない。
つまり、アトミック変数は、その値の変更操作がスレッドセーフであるといえる。

std::atomicには、専用の操作関数が用意されている。

読み込み用メンバ関数 load
書き込み用メンバ関数 store

###load-acquire
値の読み出し後の実行順序を制限する。

 while( !ready.load( std::memory_order_acquire ) ) {
 }
 
 // ここより下のコードが、
 // std::memory_order_acquire を指定した値の読み出しより、
 // 後に実行されることが保証される
 std::cout << data << std::endl;

###store-release
値の書き込み前の実行順序を制限する。

 data = 765
 // ここより上のコードが、
 // std::memory_order_release を指定した値の書き込みより、
 // 前に実行されることが保証される
 ready.store( true, std::memory_order_release );

#mutexとの違い
MUTual EXclusion (相互排他、排他制御)

mutexとは、クリティカルセクションでアトミック性を確保するための排他制御のことである。
クリティカルセクションを同時に実行するスレッドが1つだけであることを保証する。

##ロックガード
std::lock_guard
C++の標準ライブラリで提供されている仕組み。

 uint32_t g_x;
 
 std::mutex mtx;
 
 void add_value()
 {
   std::lock_guard<std::mutex> lock(mtx);
   // クリティカルセクション ここから
   g_x++;
   // クリティカルセクション ここまで
 }
 
 void safe_print()
 {
   std::lock_guard<std::mutex> lock(mtx);
   // クリティカルセクション ここから
   printf( "%d", g_x );
   // クリティカルセクション ここまで
 }

共有変数の値を安全(スレッドセーフ)に読み込む、書き出すという点では同じ効果を得られるが、
以下の2つの特徴の違いを正しく理解しておく必要がある。

  • メモリバリアは順序性を制御するものであり、クリティカルセクションを設けるものではない
  • mutexのロックによるクリティカルセクションの実行中は、他のスレッドがその実行を妨げられる
58
44
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
58
44

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?