More Related Content
PPTX
PPTX
Study ocf over_bluetooth_mesh PDF
【CNDT2020】Tunaclo API Connectで実現する次世代のクラウド間アクセス PDF
Hybrid cloud fj-20190704_final PDF
ブロックチェーンPoCにおける開発リードタイム短縮のポイント PPTX
[Cloud Native Journey ]HCCJP(ハイブリッドクラウド研究会) 第5回勉強会 PDF
PPTX
ここがつらいよ、Hyperledger Fabricの商用適用(Blockchain GIG #4発表資料) What's hot
PDF
PDF
NIFcLab Tech Laboratoryはじめます(もうすぐ) PDF
ニフクラ mobile backend チームのCIツール活用事例紹介 PDF
vm meetup_tokyo #1 NSX の運用と DFW トラブルシューティング PPTX
PDF
Mk vpp for-containers-vppug PDF
Hyperledger Fabric のプラットフォームおよびインフラ運用 PDF
クラウド上のシステム監視 入門編~システムを作ったその先に~ PDF
devsummit_nifcloud_vmware PDF
Mk onic data-intensive-public PDF
PPTX
2016-ShowNet-対外接続(エクスターナル) PDF
20181129 Twilio Business Seminar in Osaka #2 PDF
デバイスWebAPIによるIoTの普及拡大と応用事例 PPTX
PDF
BGP as a method for Abstraction PPTX
PDF
Intellectual Highway/貞末様 講演資料 PPTX
Similar to Introduction to conccrent_lock
PDF
PPTX
TotalViewを使った代表的なバグに対するアプローチ PDF
PDF
PDF
PDF
PPTX
PDF
nakameguro_feature.cpp vol.8 PDF
C21 SQL Server のスレッド管理 by 古賀啓一郎 PPTX
Effective Java 輪読会 項目66-68 PDF
PDF
Objective-C atomicity #idevjp PPTX
PPTX
Jvm reading-synchronization PPT
PDF
PDF
ODP
PDF
PPTX
More from PIXELAcorporation
PPTX
Introduction to Functional Programming PPTX
20190620 aws summit_tokyo_2019 PPTX
Global Azure Boot Camp 2019 PPTX
PPTX
PPTX
PPTX
PPTX
4K SmartTV Works with Alexa PPTX
20181228 ncf to_azure_batch PPTX
PPTX
Introduction to conccrent_lock
- 1.
- 2.
- 3.
アジェンダ
○ 1: マルチスレッドクイズ
○2: 競合とその原因と対策
○ 3: 同期オブジェクト
○ 4: 設計/実装上の注意
○ 5: Atomic 変数操作
3Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 4.
- 5.
このコードの x がとりうる値は?
5
intx = 0;
void func1() {
for (int i = 0; i < 1000000; i++) x++;
}
int main()
{
std::thread t1([]() { func1(); }), t2([]() { func1(); });
t1.join(); t2.join();
printf("x:%dn", x);
}
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 6.
- 7.
競合 (1) :データ競合 (data race)
○ 複数スレッドで同じ変数に対して同時に Read/Write が発生する
○ 厳密には少なくとも一つが Write となる
○ 発生現象
○ 何が発生するか予想できない
○変更途中の値が Read (変数がレジスタ幅を超える場合)
○Write 順が他のスレッドの Read に反映されない
○ 原因
○コードと CPU 命令の乖離
○CPU の内部動作 (キャッシュ)
7Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 8.
競合 (1) :データ競合 (data race)
8
int my_account = 0; // 私の預金口座残高
int your_account = 100; // あなたの預金口座残高
// 送金処理
bool racy_transfer(int m) {
if (m <= my_account) { // 未定義動作の可能性あり!
my_account -= m; // 未定義動作の可能性あり!
your_account += m; // 未定義動作の可能性あり!
return true;
}
return false;
}
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 9.
競合 (2) :競合状態 (race condition)
○ 実行タイミングに依存してシステムの出力結果が変化する
○ 発生現象
○ 変数が不変条件に違反した値になる
○ タイミングによって不具合発生、sleep を入れるとクラッシュ
○ 原因
○ 設計/実装ミス
○複数のリソースに対する操作が排他されていない
○スレッド間の調停を行っていない
9Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 10.
競合 (2) :競合状態 (race condition)
10
std::atomic<int> my_account = 0; // 私の預金口座残高
std::atomic<int> your_account = 100; // あなたの預金口座残高
// 送金処理
bool unsafe_transfer(int m)
{
if (m <= my_account) {
// ★この時点でも(m <= my_account)は真?
my_account -= m;
your_account += m;
return true;
}
return false;
}
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 11.
競合への対策 (基本)
○ 操作を不可分(atomic) とする
○ atomic とは一連の操作が他スレッドから一体と見えること
○全操作が完了するまで他スレッドから変更が見えない
○操作失敗時には操作前の状態に戻る
○複数スレッドで同時に操作できない
○ 例: 複数スレッドからアクセスされるリソースを排他する
○ 前提条件を満たしてから実行する
○ 他のスレッドの入力を待つような場合
11Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 12.
正しいコード
12
int my_account =0; // 私の預金口座残高
int your_account = 100; // あなたの預金口座残高
std::mutex txn_guard;
// 送金処理: 安全なトランザクション処理
bool safe_transfer(int m)
{
std::lock_guard<std::mutex> lk(txn_guard);
if (m <= my_account) {
my_account -= m;
your_account += m;
return true;
}
return false;
}
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 13.
- 14.
代表的な同期オブジェクト
○ Mutex
○ Read-WriteLock
○ Condition Variable
○ Event
○ Semaphore
○ Volatile (正確には同期オブジェクトではない)
14Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 15.
Mutex
○ ロックすることでそれ以降のコードへのアクセス権を取得する
○ 同時にロックできるのは一つのスレッドのみ
○同じ Mutex をロックしているコード間は排他されている
○ Mutex の作り方によって同じスレッドが複数回ロックをとれる (Recursive) なもの
とできないものがある
○ パフォーマンスが要求されない限り Recursive にするのが安全
○後の修正で Recursive が必要になるかもという理由
15Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 16.
Mutex コード例 (C++)
16
intfunc()
{
static std::recursive_mutex mutex;
std::lock_guard<std::recursive_mutex>
lock(mutex);
static int count = 0;
count++;
return count;
}
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 17.
Read-Write Lock
○ Readは複数あっても race condition は起きないことを利用してパフォーマンス向
上を行う
○ Read のロック API と Write API のロックが分かれている
○ Read だけなら複数取得可
○ Write と Read, 複数の Write は同時に行えない
○ Write に比べて Read が多い場合にロック待ち時間を削減できる
17Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 18.
Read-Write Lock コード例(Win32 API)
18
void init(SRWLOCK* lock) {
InitializeSRWLock(lock);
}
static int count = 0;
int read(SRWLOCK* lock) {
// AcquireSRWLockExclusive がない限り通る
AcquireSRWLockShared(lock);
int ret = count;
ReleaseSRWLockShared(lock);
return ret;
}
int write(int val, SRWLOCK* lock) {
// 他の AcquireSRWLockExclusive, AcquireSRWLockShared がない限り通る
AcquireSRWLockExclusive(lock);
count = val;
ReleaseSRWLockExclusive(lock);
}
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 19.
Condition Variable
○ 待機側が待機し、通知側が待機側を起動させる
○待機側が待機前にロックオブジェクト (Mutex, Read-Write Lock) を握っているの
が前提となる
○ 待機と不可分に行える
○ “条件変数” とよばれているのは以下のような使われ方のため
○ 待機側は処理条件を満たしていない場合に待機する
○ 通知側は待機側の処理条件を満たした状態にして通知する
19Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 20.
Condition Variable コード例(C++)
20
static std::mutex mutex;
static std::condition_variable cond;
bool flag = false;
void wait() {
// flag が true になるまで待機。
std::unique_lock<std::mutex> lock(mutex);
cond.wait(lock, []() { return flag; });
flag = false;
}
void wake() {
// 待機スレッドを起こす
{ std::unique_lock<std::mutex> lock(mutex); flag = true; }
cond.notify_one();
}
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 21.
Event
○ 通知側が通知を行うと待機しているスレッドが起動する
○ オブジェクトのSignal (通ってよし) と Non-Signal (通行止め) を管理する
○ Signal 状態でロックとると non-Signal になる/ならないとか、初期状態セットでき
るとかバリエーションがいっぱいある
○ ので使う際には OS のリファレンスをちゃんと読む必要がある
○ 典型的な例は I/O 待ち
21Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 22.
Event コード例 (Win32API)
22
int init(HANDLE *handle) {
// 自動で non-signal になる初期値 non-signal のイベント作成
*handle = CreateEvent(/*atrtributes*/NULL,/*manual_reset*/FALSE,
/*initial_state*/FALSE, /*name*/NULL );
}
void wait(HANDLE handle) {
// signal 状態まで待つ
// 自動で non-signal になるので一度に起動するスレッドはひとつ
WaitForSingleObject(handle, INFINITE);
}
void wake(HANDLE handle) {
SetEvent(handle);
}
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 23.
- 24.
Semaphore コード例 (Win32API)
24
int func()
{
static HANDLE sem = CreateSemaphore(
/*attributes*/NULL, /*initial count*/5, /*max count*/ 12, /*name*/ NULL
);
WaitForSingleObject(sem, INFINITE); // count--, count > 0 でロックされない
// 処理
// count 増加 -> 待機スレッド起動
ReleaseSemaphore(sem, /*count*/1, /*[out] prev_count*/NULL);
}
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 25.
- 26.
Volatile コード例 (C++)
26
//デバイス側で変更されるフラグ
extern volatile int flag;
int wait() {
// busy-loop でチェック
// 外部で flag が true になれば抜ける
while (!flag) {};
}
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 27.
注意点
○ 各同期オブジェクトが使用できる/できないは OSや言語によって違う
○ 各同期オブジェクトがプロセス間で共有できる/できないは OS 依存
○ C++ の各オブジェクト操作が OS のどの API に相当するかは stdlib の実装依存となっ
ている
○ パフォーマンスが必要なら軽い API に切り替える必要がある
○ スペースの都合上ヘッダの include は省略した
○ 共有オブジェクトが static 変数になっているのはスペースの都合上なので実装ではやら
ないこと
27Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 28.
- 29.
- 30.
条件変数の spurious wakeup
○条件変数での待機処理にて通知なしでブロック解除される現象
○ 原因はライブラリ内部処理やハードウェア/OS の都合
○ 発生頻度はごく低い…はず
○ 対応 : 条件変数を使う場合は必ず待機終了後に待機条件をチェック
○ 満たしていない場合は再度待機
30Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 31.
デッドロックを防ぐために – 原因
○デッドロックは 2 個以上のロックの取得順番が違うときに発生する
○ 特に危ないケース
○ ロックを取得したまま他モジュールを呼びに行き、そのモジュールから自モ
ジュールを呼び出す経路がある場合
○同じスレッドでの呼び出しは問題ないが、異なるスレッドで
の呼び出し経路があると最悪
○ モジュール内に複数ロックオブジェクトがある場合
○常にロック順序を一定にする必要がある
31Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 32.
- 33.
デッドロックを防ぐために – 対策
○排他区間を短くする
○ モジュール内のロックオブジェクトを一つにする
○ モジュール相互の呼び出しが発生するパスを作らない
○ ロックを取得したまま他モジュールを呼ばない
○ 上位モジュールの処理は上位モジュールのスレッドで処理する
○ いったん上位モジュールのタスクキューで受ける等
○タスクキューが詰まってデッドロックになる場合も (実例)
○ 下位モジュールのロックを処理から切り離す
33Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 34.
Sleep 時間はベストエフォート
○ 各種sleep/待機関数に渡す時間はその時間での起動を保証しない
○ スレッドスケジューリング (スレッド優先度等) の都合上起動が待たされる可能性が
ある
○ また、起動の精度はタイマー割り込みの頻度に依存する
○ Windows: 16 millisecond (デフォルト状態)
○ PC Linux: 4 millisecond (2010 の情報)
34Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 35.
精密な Sleep 時間が必要な場合
○精密に起動できるようにする
○ タイマー割り込み頻度の変更 (1 msec 精度まで)
○ スレッド優先度の引き上げ
○ Busy loop の採用
35Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 36.
- 37.
- 38.
Atomic 変数の前に –data race の原因
○ コードと CPU 命令の乖離
○ CPU 内部の都合
○ キャッシュの同期が基本行われない
38Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 39.
Data race :コードと CPU 命令の乖離 (1)
○ 下の処理中でコンテキストスイッチとなった場合、
再開時は昔のメモリを読み込んだ状態で処理再開
される
39
int func(int *ptr) {
*ptr++;
}
int func(int *ptr) {
int val = *ptr;
val++;
*ptr = val;
}
おおよそ
次のように展開される
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 40.
Data race :コードと CPU 命令の乖離 (2)
○ 変数がレジスタ幅に収まらない場合、メモリ書き込
み途中で他スレッドから読み取られる可能性がある
○ 矛盾した状態を読み込んでしまう
40
struct St {
int64_t a;
int64_t b;
};
int set(St *ptr) {
*ptr = { 123, 125 };
}
おおよそ
次のように展開される
struct St {
int64_t a;
int64_t b;
};
int set(St *ptr) {
ptr->a = 123;
ptr->b = 125;
}
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 41.
Data race :CPU キャッシュ (1)
41
メインメモリ (DRAM)
L3 キャッシュ
L1 キャッシュ
L2 キャッシュL2 キャッシュ
L1 キャッシュ
Core 1 Core 2
○ メモリアクセスは遅いのでキャッ
シュ階層を設ける
○ 容量と速度の要請から複数段に分か
れている
○ Core に近いキャッシュは Core ごと
に用意されている
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 42.
Data race :CPU キャッシュ (2)
42
メインメモリ (DRAM)
L3 キャッシュ
L1 キャッシュ
L2 キャッシュL2 キャッシュ
L1 キャッシュ
Core 1 Core 2
○ ある Core でのメモリ書き込みが他
の Core に見えるまでには時間がか
かる
○ 左図では最速 44 clock 先
○ 異なる Core で同じアドレスを読み
込んでいた場合、自分のキャッシュ
にある値を読み込み書き込みに気が
付かない4 clocks
11 clocks
22 clocks
たくさん
レイテンシ
write
時間をかけて伝わる
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 43.
CPU に必要な機能
○ メモリを直接操作する
○ある Core での操作が他の Core にすぐ見える
○ キャッシュの整合性を保つ
○他の Core をストールさせる
○キャッシュ間の同期をとる
43Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 44.
Intel X86 CPU命令
○ LOCK XADD [mem] r
○ r = [mem], [mem] = [mem] + r
○ LOCK CMPXCHG [mem] r eax
○ if (eax == [mem]) { [mem] = r } else { eax = [mem] }
○ これらの命令を使うことで core 間の同期をとりつつ処理できる
44Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 45.
C++ atomic 変数
○Atomic CPU 命令をラップしてくれるクラス
○ 変数への操作を適切な atomic CPU 命令に変換してくれる
○ アーキテクチャによって提供されるビット幅等が違う
45
std::atomic<int> val = 128;
void func() {
int chk = 256;
val.compare_exchange_weak(chk, 1024); // lock cmpxchg
val++; // lock xadd
}
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 46.
C++ atomic 変数
○利点 : data race を除去できる
○ 欠点 : ある程度のオーバーヘッドがある
○ 常にメモリ上に変数が配置されるうえに Cache の整合性を保つ処理が入る
○同じキャッシュラインへの操作はストールする
○ 使い方
○ #include <atomic> する
○ atomic にしたい変数を std::atomic<type> とする
○アーキテクチャによって type に入れれる型は違う
46Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 47.
応用 :lock-free アルゴリズム
○Compare and Swap (CAS) を利用して読み取り値が変わらない場合のみ操作成功
させる
○ Intel CPU の命令は CMPXCHG だが一般的な用語は Compare and Swap
○ 操作中に割り込みが入った場合 CAS が成功しないので (事前に読み取った値と異な
る値が返される) 成功するまでリトライを繰り返す
47Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 48.
Lock – freeアルゴリズム例
○ 現在値が引く値よりも大きければ引く操作
○ Compare and exchange に成功すれば ret には tmp が入るはず
○ (ret == tmp) で成功判定できる
48
std::atomic<int> val = 128;
void minus(int sub) {
int tmp = 0; int ret = 0;
do {
ret = tmp = val;
if (sub <= tmp) {
ret = val.compare_exchange_weak(tmp, tmp - sub);
}
} while (ret != tmp);
}
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 49.
応用 : SpinLock
○Mutex と機能は同じだがビジーウェイトによってロック解放を待つ
○ ロック対象処理が短い場合に Mutex よりも反応性がよい
○ シングルコア CPU では意味がない
○ 実装は atomic 変数の操作で行う
49Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.
- 50.
SpinLock 実装
50
class spinlock{
private:
std::atomic_flag state_;
public:
spinlock() : state_(ATOMIC_FLAG_INIT) {}
void lock() {
// busy-wait で現在の状態をロック状態にする
while (state_.test_and_set(std::memory_order_acquire));
}
void unlock() {
state_.clear(std::memory_order_release); // 値をアンロック状態にする
}
};
int func() {
static spinlock lock;
std::lock_guard<spinlock> lk(lock);
static int val = 0; return val++;
}
Copyright © PIXELA CORPORATION. All Rights Reserved.|PIXELA CORPORATION PROPRIETARY AND CONFIDENTIAL.