1

この件についてアドバイスをいただければ幸いです。

例えば

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;
    }
};

事前にコメントやご意見をお寄せいただきありがとうございます。

ファティ

4

4 に答える 4

2

これはマルチスレッドの中心的な問題です。クライアント コードがオブジェクトをスレッド セーフな方法で使用することを要求することはできません。また、クライアント コードが成功の落とし穴に陥るのを助けるために何もすることもできません。ロックを自分で処理する必要があります。コードを正しく機能させる可能性が最も低い人に、コードを機能させる責任を負わせます。

アクセサーからオブジェクトのコピーを返すことで、簡単にすることができます。これはスレッドセーフです。1 つのスレッドが所有するコピーは 1 つだけです。オブジェクトを変更しても望ましい結果が得られないことを補強するために、おそらくそのコピーの型を不変にする必要があります。ひどく噛む可能性のある解決できない副作用は、このコピーが定義上古くなっていることです。これらは、良いことよりも害を及ぼす可能性が高いバンドエイドです。

クライアント プログラマーが何をすべきかを理解できるように、メソッドのスタッフィングを文書化します。

于 2010-12-15T13:32:06.733 に答える
2

オブジェクトが正しいタイミングでロックおよびロック解除されることを保証するために、設計はユーザーに責任を負わせます。また、ロックされたオブジェクトはエラー チェックを行っていますが、すべてのベースをカバーしているわけではありません (オブジェクトを使い終わったときにオブジェクトを解放するのを忘れるなど)。

Unsafe object があるとしましょうTData。これを にラップしますが、へのポインタを返すFoo代わりに、すべてのパブリック メソッドを に再実装しますが、ロックとロック解除を使用します。FooTDataTDataFoo

これは、インターフェイスが実装を呼び出す前にロックを追加することを除いて、 pImplパターンに非常に似ています。このようにして、ユーザーはオブジェクトがスレッドセーフであることを認識し、同期について心配する必要はありません。

于 2010-12-15T12:51:02.350 に答える
2

デメテルの法則について聞いたことがありますか?

同様のアドバイスがあります (Sutter からだと思います): Don't share references to your internals

内部への参照を共有することにより、パブリック インターフェイスが実装の詳細をリークすることになるため、どちらも結合を回避することを目的としています。

これが言われたので、あなたのインターフェースは機能しません。

問題は、オブジェクトではなくプロキシをロックしていることです。複数のパスからアクセスできます。

  • からFoo、ミューテックスは必要ありません --> うん?
  • 2つの異なるものからlocked_object--> これは意図的ではないようです...

さらに重要なことに、一般にオブジェクトの 1 つの部分をロックすることはできません。これは、オブジェクト全体のトランザクション セマンティクスを持つことができないためです。

于 2010-12-15T13:29:17.137 に答える
1

これは特に安全ではありません。ユーザーが物事を故障させるのを妨げるものは何もありません。

locked_object<T> o(...);
T* t = o.get();
o.unlock();
t->foo(); // Bad!

もちろん、上記のコードがなぜ悪いのかは簡単にわかりますが、実際のコードははるかに複雑であり、ロックが解除された後にポインターがぶら下がる可能性があり、ピン留めするのがはるかに困難です。

于 2010-12-15T12:46:00.180 に答える