
◇ CThreadとは?
ブロックチェーンはマルチスレッドで稼動しておりますのでスレッドの管理が必要です。旧コアはboostのスレッドを毎回立ち上げで、最新コアはCThreadによる管理でした。
※ その他、LOCK(), TRY_LOCK()などのbitcoin用のマクロを利用可能とします。
◇ LOCK(), LOCK2(), LOCK3()とは?
マルチスレッドの場合は共有された変数にアトミック性が求められます。この共有される変数にはstatic変数, メンバ, externのグローバル変数があります。これらの変数を読み書きする前にマクロ(LOCK, LOCK2, LOCK3)を挟みます。すると定義された「スコープ内」において「ミューテックスの処理」が実装されます。この作用により複数のスレッドが同時に対象スコープへ突入しても一つずつ排他的に処理されるようになります。
◇ TRY_LOCK()とは?
呼ばれた瞬間、LOCKできるのかを試すマクロです。LOCK不可でもエラーではないため「待てば」ロックできます。しかし全体の処理速度に影響が出ます。独立した複数の処理を行う場合このマクロでロックできたスコープからの処理を組めば最速になります。
※ win_threadの例では、::WaitForSingleObjectの第二引数に「INFINITE以外」を指定した場合です。その場合は指定された時間までは待機しますがそれを越えるとロック失敗で制御が戻ってきます。
◇ ENTER_CRITICAL_SECTION(), LEAVE_CRITICAL_SECTION()とは?
スコープを超えてロックしたいときに使います。ただしENTERしてLEAVEを忘れるとデッドロックいたします。そのため、毎回自分でLEAVEを書くスタイルではなくデストラクタにLEAVEをさせる構造にします。
◇ イベントとは?
スレッドを待機させるオブジェクトをイベントと呼びます。ここで間違ってもスレッドを待機させるときにfor(;;)のようなコードを書いてはいけません(そのようなことをしてもスレッドコンテキストが切り替わる保証はなく、無限ループになる可能性があります)。そのため、必ずイベントオブジェクトを置いてそのイベントのシグナルを制御します。
イベントにより非シグナルなら待機状態・シグナルなら通過状態という制御が行われます。イベントを仕掛けた後非シグナルにしておいて、他の処理を終えた直後にシグナル状態に遷移させるといった使い方です。
◇ ミューテックスとは?
ミューテックスとは複数のスレッド通過を一つずつにするためのオブジェクトです。ところでこのミューテックスには2タイプが存在いたします。それらは再帰可能なミューテックスと待機可能なミューテックスです。再帰可能なミューテックスは同じスレッドIDをロックしないためデッドロックを防ぐことができます。よって待機に対して必要性を感じない大部分の場面では再帰可能なミューテックスを使うべきです。
再帰可能なミューテックス:
再帰可能にするため同じスレッドIDを持つスレッドが同じミューテックスを通過してもロックされない仕様です。よってイベントによる待機制御はできません。
待機可能なミューテックス:
同じスレッドIDを持つスレッドが同じミューテックスを通過するときであってもロックされます。その性質からイベントによる待機制御は可能です。しかし使い方を誤るとデッドロックしやすくその使用は限定的にすべきです。bitcoinの定義では、再帰可能はCCriticalSection、待機可能はCMutexになっておりました。
◇ サンプルコード
static int32_t nCounter=0; static CCriticalSection cs_main; void fn_thread(void *) { LOCK(cs_main); ++nCounter; ::fprintf(stdout, "nCounter=%d\n", nCounter); } int main(int argc, char *argv[]) { CThread thread[32]; for(int i=0; i<32; ++i) thread[i].set_fn(fn_thread, nullptr); for(int i=0; i<32; ++i) thread[i].start(); for(int i=0; i<32; ++i) thread[i].wait(); return 0; }