0

別のクラスから呼び出される単純なデータ クラスがあります。

データクラス:

class Data
{
public:
    QString getName() const
    {
        return this->mName;
    }

    void setName(AccessData* access, const QString& name)
    {
        this->mName = name;
        access->emitNameChanged(this);
    }
private:
    QString mName;
    QReadWriteLock mLock;
};

そして、ロックも処理する新しい名前を取得/設定するために使用しているクラスは次のとおりです。

class AccessData : public QObject
{
public:
    QString getName(Data* data)
    {
        QReadLocker lock(&data->mLock);
        return data->getName();
    }

    void setName(Data* data, const QString& name)
    {
        QWriteLocker lock(&data->mLock);
        data->setName(this, name);
    }

    void emitNameChanged(Data* data)
    {
        emit this->nameChanged(data);
    }
signals:
    void nameChanged(AccessData* access, Data* data);
};

何が起こるか: AccessData クラスを使用して、Data インスタンスの名前を読み書きします。AccessData クラスは、読み取り/書き込みのロックを担当します。ただし、ご覧のとおり、Data クラスは setName() メソッドで AccessData インスタンスをコールバックして、変更に関するシグナルを適切に送信します。注: これは疑似コードにすぎません。実際にはもっと複雑なので、Data クラスは呼び出し元を介してシグナルを発信できる必要があります。

そして、ここに問題があります:

「d」という「Data」のインスタンスがあるとします。 Data* d; 「AccessData」インスタンス「a」を使用して名前を変更しています: a->setName(d, "new name"); 同時に、次のコードで nameChanged() シグナルに接続しています。

...
void nameChanged(AccessData* access, Data* data)
{
    // Read the new name
    QString newName = access->getName();
}

そして、ここに問題があります:

  1. a->setName(d, "新しい名前") の呼び出し
  2. "d" は "a" によってロックされるようになりました (書き込みロック)
  3. 「d」は、まだロックされているにもかかわらず、名前の変更に関する信号を発します
  4. nameChanged シグナルに接続されているメソッドが getName() にアクセスしようとしています
  5. これにより、別の QReadLock が発行され、デッドロックが発生します。

これを適切に処理するにはどうすればよいですか? 思いついたのは次の2つです。

  1. 信号を遅延させて(別名ノンブロッキング)発信して、ループに入れます。信号をすぐにプッシュしたいので、これは私が望むものではありません。

  2. Data クラス内でロック/ロック解除のものを移動し、最初にロックを解除してから信号を送信します。これは私が望んでいるものではありません。なぜなら、Data クラスをロックから完全に解放したいからです。

何か案が?私は受胎不全ですか?

どうもありがとうアレックス

4

1 に答える 1

0

モデル内のオブジェクトが何を表しているかを考える必要があります。の哲学Dataは疑わしい。それはロックを所有していますが( has-aコンポジション)、セルフロック可能にしたくありません。が単純なDataデータ ラッパーであることを意図している場合は、ロックを所有するべきではありません。したがって、独自のロックを処理できるようにするか (その後、放出する前にロックを解除できます)、ロックと放出を からに移動します。DataAccessData

何らかの理由で提示されたデザインを保持したい場合は、 として初期化することでこれを「解決」できmLockますQReadWriteLock::Recursive。次に、同等の量のunlock(). しかし、私の個人的な経験では、再入可能ロックは暴走/誤解されたコール フローの確実な兆候であり、忍び寄る誤解が激しく噛みつくものです。再入可能ロックなしでは解決できないと思われる理論的概念については読んでいますが、実際には避けられないものを見なければなりません。

于 2013-01-07T13:20:55.123 に答える