スレッドセーフでデータメンバーを外の世界と共有する
-
10-10-2019 - |
質問
この問題についてアドバイスをいただければ幸いです。
例えば
class Foo
{
TData data;
public:
TData *getData() { return &data; } // How can we do this in a thread safe manner ?
};
だから私は作るメカニズムを持ちたいです getData()
スレッドセーフ。次のテンプレートクラスにデータメンバーをパックすることを含む独自のソリューションを思いつきました。どう思いますか ?考えられる問題は何でしょうか?
class locked_object : boost::noncopyable
{
T *object;
TLockable *lock;
bool locked;
public:
locked_object(T *_object, TLockable *_lock) : object(_object), lock(_lock), locked(true)
{
lock->lock();
}
~locked_object()
{
lock->unlock();
}
T *get()
{
_ASSERT(locked);
if (!locked)
throw new std::exception("Synchronization error ! Object lock is already released !");
return this->tobject;
}
void unlock()
{
locked = false;
lock->unlock();
}
T *operator ->() const
{
_ASSERT(locked);
if (!locked)
throw new std::exception("Synchronization error ! Object lock is already released !");
return this->tobject;
}
operator T *() const
{
_ASSERT(locked);
if (!locked)
throw new std::exception("Synchronization error ! Object lock is already released !");
return this->tobject;
}
};
事前にコメントや意見をありがとう。
ファティ
解決
聞いたことがありますか デメテルの法則 ?
同様のアドバイスがあります(サッターから): 内部への参照を共有しないでください
どちらも避けることを目的としています カップリング, 、内部への参照を共有することにより、パブリックインターフェイスが実装の詳細をリークすることを意味するためです。
これが言われたので、インターフェイスは機能しません。
問題は、オブジェクトではなくプロキシをロックしていることです。複数のパスを介してアクセスできることです。
- から
Foo
, 、Mutexは必要ありません - > Oups? - 2つの異なるから
locked_object
- >これは意図的ではないようです...
さらに重要なことは、一般的にオブジェクトの単一の部分をロックすることはできません。なぜなら、オブジェクト全体のトランザクションセマンティクスを持つことはできないからです。
他のヒント
あなたのデザインは、オブジェクトが正しい時間にロックされてロックされていることを確認するために、ユーザーにONUSにかかっています。また、ロックされたオブジェクトはエラーチェックを行いますが、すべてのベースをカバーするわけではありません(オブジェクトを終了したときにオブジェクトをリリースするのを忘れるなど)
そのため、安全でないオブジェクトがあるとしましょう TData
. 。あなたはこれを包みます Foo
しかし、その代わりに Foo
ポインターを返す TData
, 、すべてのパブリック方法を再実装します TData
の Foo
ただし、ロックとロック解除を使用します。
これは非常に似ています pimpl インターフェイスを除くパターンは、実装を呼び出す前にロックを追加します。このようにして、ユーザーはオブジェクトがスレッドセーフであり、同期を心配する必要がないことを知っています。
これはマルチスレッドの中心的な問題であり、クライアントコードがスレッドセーフでオブジェクトを使用することを要求することはできません。また、クライアントコードが成功のピットに陥るのを助けるために本当に何でもできません。それは、それ自体でロックの世話をしなければなりません。コードを正しく実行する可能性が最も低い人にコードを機能させるという責任を負わせます。
返品することで簡単にすることができます コピー アクセサからのオブジェクトの。それはスレッドセーフであり、1つのスレッドで所有されているコピーは1つだけです。おそらく、オブジェクトを変更することで望ましい結果が得られない可能性が高いことを再強化するために、そのコピーのタイプを不変にする必要があります。ひどく噛む可能性のある解決できない副作用は、このコピーが定義上古くなっているということです。これらは、善よりも害を及ぼす可能性が高いバンドエイドです。
クライアントプログラマーが何をすべきかを知っているように、メソッドの詰め物を文書化します。
これは特に安全ではありません。ユーザーが物事を故障させることを妨げるものはありません:
locked_object<T> o(...);
T* t = o.get();
o.unlock();
t->foo(); // Bad!
もちろん、上記のコードが悪い理由は簡単にわかりますが、実際のコードははるかに複雑であり、ロックがリリースされた後にポインターがぶらぶらできる方法は、ピン止めがはるかに難しい方法があります。