3

私は自分のプロジェクトのために手作りのミューテックスを作成しましたが、それがスレッドセーフかどうかは疑問です...

    bool blocked;

    while ( blocked )
    {

    }

    blocked = true;
    ...
    blocked = false;

たとえば、スレッドAがwhileループを通過し、時間内にフラグをブロックせず(フラグを設定する時間がないfalse)、スレッドBもwhileループを通過するとします。

  1. 出来ますか?なんで?

  2. 私が理解しているように、ミューテックスはまったく同じ仕事の原則を持っています。なぜこれがミューテックスで発生しないのですか?中断できないアトミック操作について読んだことがあります...では、check-if-mutex-availablemutex-blockは中断できませんよね?

4

5 に答える 5

8

あなたのコードは完全に機能していません!

その理由は、変数へのアクセスがblockedアトミックではないためです。true最初のスレッドが更新を書き出し、更新がすべてのCPUに伝播する前に、2つの読み取りが発生した場合、2つのスレッドがそれを同時に読み取り、ミューテックスのロックが解除されていると判断できます。

これを解決するには、アトミック変数とアトミック交換が必要です。atomic_flagタイプはまさにあなたが望むものです:

#include <atomic>

std::atomic_flag blocked;

while (blocked.test_and_set()) { }  // spin while "true"

// critical work goes here

blocked.clear();                    // unlock

std::atomic<bool>(または、とを使用することもできますexchange(true)が、atomic_flagはこの目的のために特別に作成されています。)

アトミック変数は、これがシングルスレッドコンテキストである場合に無関係と思われるコードをコンパイラーが並べ替えるのを防ぐだけでなく、CPU自体が一貫性のない方法で命令を並べ替えるのを防ぐために必要なコードをコンパイラーに生成させます実行フロー。

実際、少し効率を上げたい場合は、次のように、セットおよびクリア操作でより安価なメモリオーダリングを要求できます。

while (blocked.test_and_set(std::memory_order_acquire)) { }  // lock

// ...

blocked.clear(std::memory_order_release);                    // unlock

その理由は、一方向の正しい順序のみを気にするためです。他の方向の更新の遅延はそれほど費用がかかりませんが、(デフォルトのように)逐次一貫性を要求することはかなり費用がかかる可能性があります。


重要:上記のコードはいわゆるスピンロックです。これは、状態がロックされている間、ビジースピン(whileループ)を実行するためです。これはほとんどすべての状況で非常に悪いです。カーネルが提供するmutexシステムコールは、スレッドがスリープ状態になり、カーネルがスレッド全体のスケジュールを解除できることをカーネルに通知できるため、まったく異なる魚のやかんです。ほとんどの場合、これがより良い動作です。

于 2012-09-11T09:51:23.890 に答える
3
  1. プロセッサは、ループに入った後、ミューテックスをロックする前に、最初のスレッドから2番目のスレッドに切り替えることができるために可能です。
  2. カーネルレベルの操作を使用して、特定の操作が完了するまでスレッドが切り替えられないようにするため、これが可能です。

たとえば、Windowsでは、ミューテックスを次のように見せることができます

于 2012-09-11T09:53:14.263 に答える
1

あなたはすでにそれをほとんど持っています。

  1. はい、それは非常に可能です。シングルコアの場合、スレッドはタイムスライシングを介してOSによって実行されます。スレッドAを少し実行してから一時停止し、スレッドBを少し実行します。スレッドAは、whileループを通過した直後に一時停止する可能性があります。

  2. このような問題を解決するために、CPUは何によっても中断されない特別な命令を実装しました。これらのアトミック操作は、ミューテックスがフラグをチェックし、1回の操作で設定するために使用されます。

于 2012-09-11T09:51:44.400 に答える
1

はい、あなたが説明した状況が発生する可能性があります。blockedこれはfalse、テストとの間にスレッドが中断される可能性があるためblockedですtrue必要な動作を得るには、アトミックテストアンドセット操作を使用またはエミュレートする必要があります。

テストアンドセットの詳細については、こちらをご覧ください

于 2012-09-11T09:52:28.490 に答える
1

ミューテックスの実装は、相互排他性(つまり、その意味)を保証する必要があり、コードで取得されないようにする必要があります。アクセスには、アトミック変数と適切なメモリ順序が必要です。C ++ 11ではstd::mutex(理想的にはと一緒に)使用するのが最適です。C++03ではなどstd::lockを使用できます。boost::mutex

于 2012-09-11T09:54:35.120 に答える