2009-04-23 14 views
4

制約のあるデバイスでは、2つのboolで2つのスレッド間でロックを「偽装する」ことがよくあります。それぞれは1つのスレッドでのみ読み込まれ、他のスレッドによってのみ書き込まれます。私はこれをしないだろうPC上当然のことながらプリミティブ同期プリミティブ - 安全?

bool quitted = false, paused = false; 
bool should_quit = false, should_pause = false; 

void downloader_thread() { 
    quitted = false; 
    while(!should_quit) { 
     fill_buffer(bfr); 
     if(should_pause) { 
      is_paused = true; 
      while(should_pause) sleep(50); 
      is_paused = false; 
     } 
    } 
    quitted = true; 
} 

void ui_thread() { 
    // new Thread(downloader_thread).start(); 
    // ... 
    should_pause = true; 
    while(!is_paused) sleep(50); 
     // resize buffer or something else non-thread-safe 
    should_pause = false; 
} 

が、制約のあるデバイス上で、それがロックを取得するよりもはるかに速くなりbool値を読み取るようだ:ここで私が何を意味するかです。もちろん、バッファの変更が必要なときは、遅い回復のためにトレードオフします(「sleep(50)」を参照)。

質問 - 完全にスレッドセーフですか?または、私がこのようなロックを偽装するときに気づいておく必要がある隠された落書きがありますか?それとも私はこれをやってはいけないのですか?

+0

sleep(50)は*非常に遅い回復です - パラメータは秒単位です。セマフォーと比較して、私はおそらく、この方法でより多くの平均遅延を導入すると思います。 –

+0

ここでスピンロックを行うべきです。 – RandomNickName42

答えて

5

デバイスのメモリアーキテクチャとコンパイラによって生成されたコードを詳しく理解していない限り、このコードは安全ではありません。

ちょうどそれがうまくいくと思われるので、それが意味するものではありません。制限されていないタイプのような「拘束された」デバイスはますます強力になっています。私は、例えば、携帯電話でデュアルコアCPUを見つけることに対して賭けることはありません。つまり、私は上記のコードがうまくいくとは思っていません。

+0

*「携帯電話でデュアルコアCPUを見つけることには賭けていません。例えば、* 6年後にQuadCoreを使ってすでに電話で読むのはおかしくなります」 – luk2302

+0

@ luk2302 lol。私に思い出させてくれてありがとう。 –

0

スリープコールに関しては、スリープ(0)またはスレッドを一時停止する同等のコールを行うだけで、次のラインを1回転させることができます。

残りについては、デバイスの実装の詳細が分かっている場合はスレッドセーフです。

0

質問に答える。

これは完全にスレッドセーフですか?私はこれはスレッドセーフではないと私は答えているだろうと私はまったくこれをやりません。私たちのデバイスとコンパイラの詳細を知らなくても、これがC++の場合、コンパイラは自由に並べ替えて最適化します。例えばあなたが書いた:

is_paused = true;    
while(should_pause) sleep(50);    
is_paused = false; 

コンパイラはこのような何かにこれを並べ替えることを選択することがあります。

sleep(50); 
is_paused = false; 

他の人が言ってきたように、これはおそらく、シングルコアデバイスを動作しません。

ロックを取るのではなく、ちょうどにするほうが、UIメッセージの処理の途中ではなく、をUIスレッドで少なくしてください。あなたがUIスレッドに時間をかけ過ぎてしまったと思うなら、非同期コールバックをきれいに終了して登録する方法を見つけてください。

UIスレッドでスリープを呼び出す(またはロックを取得しようとしたり、ブロックする可能性のあるものを実行しようとする)場合は、UIをハングしグリッチさせるためにドアを開きます。ユーザーが気づくには50msの睡眠で十分です。また、ロックを取得したり、I/Oのような他のブロッキング操作を実行しようとすると、グリッチからハングアップするI/Oを取得するのに不確定な時間を待つという現実に対処する必要があります。

5

あなたが意図したが、this blog post by Vitaliy Liptchinskyで説明したように2つの隠し落とし穴が実際に存在しているとして、スレッド間の通信にブール値を使用して作業することができます:

キャッシュ・コヒーレンシ

CPUは常にからメモリ値をフェッチしません。 RAM。ダイ上の高速メモリキャッシュは、CPU設計者がVon Neumann bottleneckを回避するために使用するトリックの1つです。いくつかのマルチCPUまたはマルチコアのアーキテクチャ(Intelの​​のような)では、これらのCPUキャッシュは共有されず、自動的に同期されません。言い換えれば、異なるCPUで実行されている場合、同じメモリアドレスのスレッドが異なる値を参照している可能性があります。

あなたは 揮発性C++C#java)、または実行 explicit volatile read/writesとしてあなたの変数を宣言するか、ロック機構を利用するために必要なこれを避けるために。

コンパイラの最適化

コンパイラやジッタは、複数のスレッドが関与している場合は安全ではないの最適化を行うことができます。例については、リンク先のブログ記事を参照してください。ここでも、コンパイラに通知するためにvolatileキーワードやその他のメカニズムを使用する必要があります。

+0

+1は、揮発性とキャッシュの一貫性を保ちます。 – RBerteig

+0

揮発性はメモリ障壁を意味しません。それは助けにならないでしょう。実際にはC言語の一部としては利用できませんが、C1xでは(_Atomic型として)利用可能である適切な同期プリミティブが必要です。それまでは、適切なロック命令、またはスレッドが実装しているプリミティブ(pthreadなど)とともにasmを使用する必要があります。 –

+0

@R ..: 'volatile'を使うとスレッドは同じメモリアドレスに対して異なる値を見るかもしれないと言っていますか?他の問題について話していますか? –

0

このコードはほとんどすべての状況で安全ではありません。マルチコアプロセッサでは、boolの読み取りと書き込みがアトミックな操作ではないため、コア間のキャッシュ一貫性はありません。これは、最後の書き込みからのキャッシュがフラッシュされていない場合、各コアがキャッシュ内で同じ値を持つことを保証していないことを意味します。

しかし、リソースが制約されたシングルコアデバイスであっても、スケジューラを制御できないため、安全ではありません。ここでは例を示しますが、わかりやすくするために、これらはデバイス上の唯一の2つのスレッドであるとふりまとうつもりです。

ui_threadが実行されると、次のコード行が同じタイムスライスで実行される可能性があります。

quitted = false; 
while(!should_quit) 
{ 
    fill_buffer(bfr); 

スケジューラはfill_buffer戻る前にdownloader_threadをpremptsしてから実行しますui_threadをアクティブにしますdownloader_threadは、次の実行され、それが次の行が実行されているタイムスライス年代に

// new Thread(downloader_thread).start(); 
// ... 
should_pause = true; 

while(!is_paused) sleep(50); 
// resize buffer or something else non-thread-safe 
should_pause = false; 

サイズ変更バッファの操作は、downloader_threadがバッファをいっぱいにしている間に実行されます。これは、バッファが破損しているとすぐにクラッシュする可能性があることを意味します。毎回発生することはありませんが、is_pausedをtrueに設定する前にバッファをいっぱいにしているという事実により、それは起こりやすくなります。しかし、downloader_threadで2つの操作の順序を入れ替えたとしても、バッファを破損するのではなく、デッドロックする可能性があります。

ちなみに、これはスピンロックの一種で、機能しません。スピンロックはプロセッサを回転させる多くのタイムスライスにまたがる可能性が高い待機時間ではありません。あなたのimplmentationは少し良いですが、スケジューラはまだあなたのスレッドを実行する必要があり、スレッドのコンテキストスイッチは安くはない睡眠を行います。クリティカルセクションまたはセマフォを待っている場合、リソースが解放されるまで、スケジューラはスレッドを再びアクティブにしません。

特定のプラットフォーム/アーキテクチャ上では、これを何らかの形で取り除くことができますが、追跡が非常に難しい間違いを犯すのは本当に簡単です。

関連する問題