0

スレッドから呼び出すことができるように、時間のかかる関数をリファクタリングしていますが、問題に頭を悩ませています(スレッドプログラミングにあまり詳しくありません)。

ユーザーはいつでもキャンセルでき、機能は停止します。データの整合性に問題が発生する可能性があるため、ユーザーがキャンセルした直後にスレッドを強制終了したくありません。代わりに、関数のいくつかの場所で、関数がキャンセルされているかどうかを確認し、キャンセルされている場合は終了します。私はそれを、安全に終了できるとわかっている場合にのみ行います。

関数のコード全体がミューテックス内にあります。これは私が念頭に置いている擬似コードです:

SomeClass::SomeClass() {
    cancelled_ = false;
}

void SomeClass::cancelBigSearch() {
    cancelled_ = true;
}

void SomeClass::bigSearch() {
    mutex.lock();

    // ...
    // Some code
    // ...

    // Safe to exit at this point
    if (cancelled_) {
        mutex.unlock();
        cancelled_ = false;
        return;
    } 

    // ...
    // Some more code
    // ...

    if (cancelled_) {
        mutex.unlock();
        cancelled_ = false;
        return;
    }   

    // ...
    // Again more code
    // ...

    if (cancelled_) {
        mutex.unlock();
        cancelled_ = false;
        return;
    }   

    mutex.unlock();
}

したがって、ユーザーが検索を開始すると、新しいスレッドが。を呼び出しますbigSearch()。ユーザーがキャンセルすると、cancelBigSearch()が呼び出され、cancelled_フラグが設定されます。その後、bigSearch()安全に終了できるポイントに到達すると、終了します。

これがすべてスレッドセーフであるかどうかはわかりますか?

4

3 に答える 3

3

cancelled_チェックと設定が同時に行われないように、別のミューテックスでアクセスをロックする必要があります。それ以外は、あなたのアプローチはOKだと思います

更新: また、から例外をスローできないことを確認してSomeClass::bigSearch()ください。そうしないと、ミューテックスがロックされた状態のままになる可能性があります。すべての戻りパスがミューテックスのロックを解除することを確認するには、コードの処理部分を で囲み、if (!cancelled_)メソッドの最後 (unlock()ミューテックスで 1 つの呼び出しがある場所) でのみ返すことをお勧めします。

さらに良いことに、mutex を RAII ( Resource Allocation Is Initializationの頭字語) オブジェクトでラップすると、関数がどのように終了しても (例外またはその他)、mutex のロックが解除されることが保証されます。

于 2012-05-20T14:29:13.030 に答える
2

はい、これはスレッドセーフです。しかし:

  1. プロセッサは個別のキャッシュを持つことができ、独自のコピーをキャッシュすることができますcancelled_。通常、mutex 同期関数は適切なキャッシュ同期を適用します。
  2. コンパイラによって生成されたコードは、データの局所性について無効な仮定を行う可能性があり、これにより、時間内に更新されない可能性がありcancelled_ます。いくつかのプラットフォーム固有のコマンドがここで役立ちます。または、単に他のメカニズムを使用することもできます。

これらはすべて、希望どおりにキャンセルされないスレッドにつながります。

コードの使用パターンは単純な「シグナリング」です。したがって、シグナルをスレッドに転送する必要があります。信号パターンは、同じトリガー (信号) を複数回トリガーし、後でクリアすることができます。

これは、次を使用してシミュレートできます。

  • アトミック操作
  • ミューテックス保護変数
  • 信号同期プリミティブ
于 2012-05-20T14:46:32.440 に答える
1

スレッドセーフではありません。あるスレッドが同時にcancelled_のスレッドが書き込む可能性があるためです。これはデータ競合であり、未定義の動作です。

他の人が示唆したように、アトミック型を使用するかcancelled_、別のミューテックスで保護してください。

また、RAII タイプを使用してミューテックスをロックする必要があります。

例えば

void SomeClass::cancelBigSearch() {
  std::lock_guard<std::mutex> lock(cxlMutex_);
  cancelled_ = true;
}

bool SomeClass::cancelled() {
  std::lock_guard<std::mutex> lock(cxlMutex_);
  if (cancelled_) {
    // reset to false, to avoid caller having to lock mutex again to reset it
    cancelled_ = false;
    return true;
  }
  return false;
}

void SomeClass::bigSearch() {
  std::lock_guard<std::mutex> lock(mutex);

  // ...
  // Some code
  // ...

  // Safe to exit at this point
  if (cancelled())
    return;

  // ...
  // Some more code
  // ...

  if (cancelled())
    return;

  // ...
  // Again more code
  // ...

  if (cancelled())
    return;
}
于 2012-05-20T23:46:16.190 に答える