2017-06-22 24 views
0

私はマルチスレッドアルゴリズムを開発しており、C#でスレッド間でクラスメンバーを共有することにいくつかの疑問があります。C#:スレッド間でクラスメンバーを共有する

アルゴリズムとプロセッサの2つのクラスがあるとします。プロセッサは メインメソッドDoWorkと追加のAvaliableResourcesメソッドがあり、処理のために使用可能なリソースの数を変更することがあります。 方法実行アルゴリズムオブジェクトでUpdateResourcesは、おそらく別のコアで作業し、2つの異なるスレッドによって呼び出されます。

はそれが可能変数は、CPUのキャッシュに保存され、メモリにアップロードされることはありません_processorされ、_processorが第二のスレッドのためにnullであるため、AvaliableResourcesが呼び出されることはありませんでしょうか?

class Processor 
{ 

    public void DoWork() { ... } 
    public void AvaliableResources(int x) { ... } 
} 

class Algorithm 
{ 
    private Processor _processor; 

    public void Run() 
    { 
     _processor = new Processor(); 
     _processor.DoWork(); 
     _processor = null; 
    } 

    public void UpdateResources(int x) 
    { 
     _processor?.AvaliableResources(x); 
    } 
} 

同期の問題がある場合は、次のコードは、それのための解決策だろうか?

オルタナティブ1

class Processor 
{ 
    public void DoWork() { ... } 
    public void UpdateResources(int x) { ... } 
} 

class Algorithm 
{ 
    private volatile Processor _processor; // added volatile keyword 

    public void Run() 
    { 
     _processor = new Processor(); 
     _processor.DoWork(); 
     _processor = null; 
    } 

    public void UpdateResources(int x) 
    { 
     _processor?.UpdateResources(x); 
    } 
} 

オルタナティブ2

class Processor 
{ 
    public void DoWork() { ... } 
    public void UpdateResources(int x) { ... } 
} 

class Algorithm 
{ 
    private Processor _processor; 

    public void Run() 
    { 
     _processor = new Processor(); 
     Thread.MemoryBarrier(); // Added memory barier 
     _processor.DoWork(); 
     _processor = null; 
    } 

    public void UpdateResources(int x) 
    { 
     Thread.MemoryBarrier(); // Added memory barier 
     _processor?.UpdateResources(x); 
    } 
} 

編集: あなたはコメントで示唆したように、より良い説明のコードを参照してください。

class Processor 
    { 
     private int resources = Environment.ProcessorCount; 

     public void DoWork() 
     { 
      /*do some long running job using avaliable resources*/ 
     } 

     public void UpdateResources(int x) 
     { 
      resources = x; 
     } 
    } 

    class Algorithm 
    { 
     private volatile Processor _processor; 

     public void Run() 
     { 
      _processor = new Processor(); 
      _processor.DoWork(); 
      _processor = null; 
     } 

     public void UpdateResources(int x) 
     { 
      _processor?.UpdateResources(x); 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      var algorithm = new Algorithm(); 
      var result = Task.Run(() => algorithm.Run()); 
      // The resources were required for other staff and are reduced 
      algorithm.UpdateResources(1); 
      // The resources are reassigned for the long running algorithm 
      algorithm.UpdateResources(10); 
      // wait until the algorithm finishes 
      result.Wait(); 
      // this update should have no effect as the Run method has finished and _processor is null 
      algorithm.UpdateResources(10); 
     } 
    } 
+0

コードを実行するとどうなりますか? – Fabulous

+0

また、https://stackoverflow.com/questions/3556351/why-we-need-thread-memorybarrierがクエリ「 – Fabulous

+0

」を「第2スレッド用」に指定していない場合も考慮してください。どちらのスレッドも第1または第2のスレッドではありません。異なるスレッド。 「2番目」のスレッドは、 'Run'も呼び出される前に必要なすべてを行っている可能性があります。その場合、' _processor'の値は何ですか? –

答えて

0

この方法で _processor objにアクセスする場合は、2つの選択肢が間違っています。

DoWorkが実行されて高速に終了するため、どのような場合でもプロセッサにはnullを設定する必要があります。 スリープ状態の最初のスレッドと同じ秒を探し、使用可能かどうかを確認してください。

あなたのコードは正しい方法を示唆するのに十分な情報を提供していません。

は、揮発性のキーワードを使用する権利ですが、ファイル名を指定して実行が可能な高速であればある決して

EDIT

[OK]を、良い例を呼び出しません。しかし、問題は、あなたがUpdateResourcesが実行される前にRunの終了を持つ主要な作業者です。

待機する前にUpdateResourcesが2回実行されるようにするには、待機後に_processor= nullを実行する新しいメソッドを挿入します。 このコードを使用するすべての方法で、待機する前にUpdateResourcesを呼び出すたびに、_processorはnullになります。

待機する前に必要なものに依存する

+1

'volatile'は非常に微妙で微妙な獣です。人々が「揮発性」の「効果」として考えていることのほとんどは、実際には副次的な副作用と実装の詳細です。誰かに 'volatile * 'が実際に何を意味するのか、そしてそれがどのように特定の状況に役立つのかを定義するように求める人は、たいていのCPUエキスパートを除いて、ほとんどの人が良い答えを出すことができません。私は "良い答えを与えることができません"というグループに自分自身を含めます –

+0

このスレッドで何が欲しいのですか?あなたの問題やその他のためのヒントが必要な場合。 Volatileはvariabileで常に最新の更新値を持つために使用されます。このキーワードは、マルチスレッドアクセスのための最適化のためのコンパイラとの通信に使用されます。とにかく、コードが実行されていない場合、更新リソースを実行しません。メソッドの更新リソースは、doWorkの実行中に呼び出された場合にのみ呼び出されます。 – Pasalino

+0

@Pasalinoより良い例を追加しました。 –

関連する問題