私は2つのセット(入力と出力)を70個の32ビット整数変数と70個のブール(全部で140個の変数)持っています。これらは、3つのスレッドからアクセスして変更する必要があります。 これらの140個の変数のすべてへのスレッドセーフな読み取り/書き込みアクセスを、単一のミューテックスの下ですべてロックせずに容易にする適切な設計パターンは何ですか(これはパフォーマンスが低下することを期待しています)?
パフォーマンス要件に関する詳細:
スレッド1(「CANシリアル通信」)は、70の入力共有変数の1つに対する更新された値を含むパケットを1msごとにハードウェアセンサーから受信します。スレッドはその値で変数を更新します。また、5msのスレッド1ごとに、70個の出力変数すべてのコピーを作成する必要があります。
スレッド2(「コントローラー」)は、10msごとにすべての入力変数のコピーを作成し、すべての出力変数を上書きします。
スレッド3(「GUI」)は、500msごとにすべての入出力変数のコピーを作成します。
システムは ARM Cortex-A8 600Mhzで動作します。
1つの解決策は、140の変数ごとにミューテックスロックを作成することですが、これはハックのように感じられます。次に、変数を140のゲッターとセッターでクラスにラップしますが、これも醜いようです。
std::atomic
:
他の選択肢はstd::atomic
。しかし、私はそれが高度で複雑な機能であると感じています。たとえば、IRCで次のスニペットの例はスレッドセーフではないことを直感的に見ても、
typedef struct MyStruct {
std::atomic<int> a;
std::atomic<int> b;
}
std::atomic<MyStruct> atomic_struct;
atomic_struct.a = 1;
atomic_struct.b = 2;
// Make a copy of `atomic_struct`
Mystruct normal_struct;
normal_struct = atomic_struct;
// Edit the values of the copied struct and copy the changes back to the `atomic_struct`.
normal_struct.a = 100;
normal_struct.b = 200;
atomic_struct = normal_struct;
コメントが答えに変わりました:
1つのミューテックスの下ですべてをロックすることでパフォーマンスを心配するのは正しいことですが、より良い解決策は、ロック内で可能な限り少ないことを確認することです。
スレッド1は値とインデックスの準備ができている必要があり、実際には単一の書き込みのみを実行します。スレッド2は、クラスの非共有ローカルインスタンスで動作し、それを共有インスタンスと交換します。スレッド3には、共有を各更新にコピーする非共有ローカルインスタンスもあります。
特定のケース(すべての変数がスカラー、つまり整数またはブール値)の場合、C++ 11の atomic 機能の使用を検討できます。最近のGCCまたはClangコンパイラが必要です。
したがって、std::atomic_bool
を使用します。これらの変数のタイプについてはstd::atomic_int
など... atomic_load & atomic_store を使用します。単純な使用法は、これらのアトミック変数へのすべてのアクセス(または割り当て)に対してatomic_load
&atomic_store
を体系的に使用することです。
変数のセット全体を処理する操作(たとえば、すべての変数のコピーを作成する)の場合、グローバルミューテックスが必要になります。
もちろん、パフォーマンスの低下はありますが、明示的なミューテックスを使用するよりもはるかに低くなります(àla std :: mutex )。私はあなたのプラットフォームを正確には知りませんが、各アクセスは通常の非原子データの場合よりも数十倍遅くなると思います。ベンチマークする必要があります。ほとんどのミューテックス実装(少なくともLinuxでは)は、 futex(7) のようなより複雑な機械に結合されたアトミックスカラーに相当するマシンを使用しています(これが、実際にはミューテックスがアトミックより遅い理由です)。
または、(意味のある方法で)変数を一緒にグループ化することを検討し、変数の小さなサブセット(たとえば、半ダース)を制御するミューテックスを用意します。