2011-12-16 14 views
-7

ここに私のワークステーションで試していたコードがあります。C#でVOLATILEキーワードが本当に必要ですか?

class Program 
{ 
    public static volatile bool status = true; 

    public static void Main() 
    { 
     Thread FirstStart = new Thread(threadrun); 
     FirstStart.Start(); 

     Thread.Sleep(200); 

     Thread thirdstart = new Thread(threadrun2); 
     thirdstart.Start(); 

     Console.ReadLine(); 

    } 

    static void threadrun() 
    { 
     while (status) 
     { 
      Console.WriteLine("Waiting.."); 
     } 
    } 

    static void threadrun2() 
    { 
     status = false; 
     Console.WriteLine("the bool value is now made FALSE"); 
    } 
} 

ご覧のとおり、Mainには3つのスレッドが発生しています。その後、ブレークポイントを使ってスレッドを追跡しました。私の最初の構想は、3つのスレッドが同時に起動されることですが、私のブレークポイントの流れは、スレッド実行フローが次々と続いていくことを示していました(スレッドのトップからボトム実行という出力フォーマットもそうでした)。みんななぜそれが起こっているのですか?

さらに、宣言でvolatileキーワードを使わずに同じプログラムを実行しようとしましたが、プログラムの実行に変更はありませんでした。 volatileというキーワードは実用的ではないとは思わない。私はどこかで間違っていますか?

+0

デバッガを使用するとどうなると思いますか? – JonH

+1

なぜスレッドは同時に実行されると思いますか? – Alan

+0

それでは、使用しているときと使用していないときに、出力に違いはありませんか? –

答えて

3

[OK]を、私はできるだけ短く、非常に長い物語を説明しよう100回のテストのうちどれも失敗したため、正常に動作します:間違っています!スレッドは、完全に非決定論的(いくつかはランダムであると思われる)の仕方で動作し、そのようなプログラムが正しく実行されることを確認するためには、さまざまな方法が必要です。

番号2:これを削除してデバッグモードでプログラムを実行してから、リリースモードに切り替えると、volatileの使用が明確になります。私はあなたが驚きを持っていると思う...リリースモードで何が起こるかは、コンパイラがコードを最適化することです(これには並べ替え命令と値のキャッシュが含まれます)。あなたの2つのスレッドが異なるプロセッサコアで動作する場合、statusの値をチェックしているスレッドを実行しているコアは、それを繰り返しチェックするのではなく、その値をキャッシュします。他のスレッドはそれを設定しますが、最初のスレッドは変更を見ません:デッドロック! volatileは、このような状況の発生を防止します。

ある意味では、volatileは、マルチスレッドのシナリオでは、実際にコードが実行されるとは限りません(そして、おそらくそうではありません)。

+0

これはいくつかの点でやや誤解を招いています。それは問題ではなく、デバッガを付けずに実行するリリースモードではありません。別のコアで動作しているかどうかは関係ありませんが、タイムシェアリングのマルチスレッディングはまったく同じ問題を抱えています。なぜなら、面倒なグローバルオーダリングの問題ではなく、キャッシュからの再読み込みではなく、だから変わらない "。 – harold

+1

@harold:私はデバッガの点では違うと思う。デバッガを接続しても問題はありません。最適化の有無にかかわらずコンパイルする必要があります。 IDEを使用しなくてもこの問題を再現できます。 – Tudor

+0

これはCにとっては真実ですが、C#コンパイラは最適化フラグが何を言っているにもかかわらず、とにかく最適化しません。それはすべてのJITコンパイラの欠陥です。 – harold

2

あなたの単純なコードがvolatileでdirrefentlyの動作をしないという事実は何も意味しません。あなたのコードは単純すぎて、揮発性とは関係ありません。メモリ競合状態がはっきりと見えるように、非常に計算量の多いコードを書く必要があります。

また、volatileキーワードは、他のプラットフォームのx86/x64以外のプラットフォームでも役立ちます。 (例えばItaniumのような意味です)

Joe Duffyはvolatileに関する興味深い情報をブログに書いています。私はそれを読むことを強く勧めます。

+0

皆さんありがとうございました...私は、スレッドの実行は完全にNONDETERMINISTICで、プロセッサの種類とスピードとコードの最適化手法に依存していると結論づけました。また、揮発性のユーザビリティは、大規模なコンピューティングマシンでは認識できますが、小さなデバッガでは認識できません。ハッピーコーディングをもう一度お願いします。 –

4

あなたの思考方法には欠陥があります。

スレッド関連の問題の本質は、それらが非決定論的であるということです。これは、あなたが観察したことが、潜在的に将来何が起こるかの指標ではないことを意味します。

これはマルチスレッドプログラミングが "難しい"理由の本質です。それはしばしば特別なテスト、あるいはほとんどの単体テストには挑戦しません。効果的に行う唯一の方法は、ソフトウェアとハ​​ードウェアのスタック全体を理解し、ステートマシンを使用して発生する可能性のあるすべての要素を示すことです。

要約すると、スレッドプログラミングは、あなたが何が起こったかについてではなく、それが起こる可能性があります。

ナンバー1:デバッガでのスレッドの動作を検査しようとすると、繰り返しマルチスレッド化されたプログラムを実行していることを結論としてとして有用である

0

次に、ブレークポイントを使用してスレッドを追跡しました。最初の概念は ですが、3つのスレッドが同時に起動されるのはすべてでしたが、私の ブレークポイントフローは、スレッド実行フローが の後に続いていることを示していました(出力フォーマットはスレッドの先頭から最後まで でした)。みんななぜそれが起こっているのですか?

デバッガはデバッグを容易にするためにスレッドを一時的に中断しています。

私はvolatileキーワードが実用的でないことを疑っています。 どこか間違っていますか?

Console.WriteLine呼び出しが が問題をマスキング を固定可能性が非常に高いです。彼らは暗黙のうちに必要なメモリバリアを生成する可能性が最も高いです。実際にはvolatilestop変数を宣言するために使用されていないときに問題があることを示す非常に簡単なコードスニペットがあります。

リリース構成で次のコードをコンパイルし、デバッガの外で実行します。

class Program 
{ 
    static bool stop = false; 

    public static void Main(string[] args) 
    { 
     var t = new Thread(() => 
     { 
      Console.WriteLine("thread begin"); 
      bool toggle = false; 
      while (!stop) 
      { 
       toggle = !toggle; 
      } 
      Console.WriteLine("thread end"); 
     }); 
     t.Start(); 
     Thread.Sleep(1000); 
     stop = true; 
     Console.WriteLine("stop = true"); 
     Console.WriteLine("waiting..."); 
     t.Join(); 
    } 
} 
関連する問題