2016-11-02 1 views
1

私はこのタスクのためのよりよい解決策があるのだろうか。あるスレッドがある量のスレッドによって同時に呼び出される関数を持っていますが、あるスレッドがすでにコードを実行している場合、他のスレッドはコードのその部分をスキップし、そのスレッドが実行を終了するまで待つ必要があります。ここで私は今のところ持っているものです。"do or wait and skip"と並行して行うより良いアプローチ

int _flag = 0; 
readonly ManualResetEventSlim Mre = new ManualResetEventSlim(); 

void Foo() 
{ 
    if (Interlocked.CompareExchange(ref _flag, 1, 0) == 0) 
    { 
     Mre.Reset(); 
     try 
     { 
      // do stuff 
     } 
     finally 
     { 
      Mre.Set(); 
      Interlocked.Exchange(ref _flag, 0); 
     } 
    } 
    else 
    { 
     Mre.Wait(); 
    } 
} 

私が達成したいどのようなことは実行速度が速く、低オーバーヘッドときれいな外観です。

+0

私は興味がありますが、方法の最後にWait()にどのようなシナリオがありますか?言い換えれば、そのスレッドはなぜ待ちますか? – brakeroo

+0

メソッドは実際に何をしていますか? 'ActionBlock 'を使って、実行のためにタイプTのメッセージを1つずつキューに入れることができます。または一度に1つの入力で動作し、後続のブロックに出力を送信する 'TransformBlock ' –

+0

並行処理の問題を処理する最善の方法は、生のスレッドを完全に避けることです。 .NETには、ほとんどの同時シナリオを実装するクラスが含まれています。ほとんどの場合、既にBCL –

答えて

-1

最初に思いつくのは、ロックを使用するように変更することです。これはコードをスキップすることはありませんが、最初のスレッドがその処理を実行している間に、各スレッドがスレッドを停止させる原因となります。このようにして、ロックは例外の場合に自動的に解放されます。

object syncer = new object(); 
void Foo() 
{ 
    lock(syncer) 
    { 
     //Do stuff 
    } 
} 
0

これを行うには、AutoResetEventBarrierの組み合わせを使用することができます。

AutoResetEventを使用して、1つのスレッドだけが「仕事」メソッドに入るようにすることができます。

Barrierは、すべてのスレッドが「仕事」メソッドに入ったスレッドが返されるまで待つようにするために使用されます。しかし、それはうまくTask Parallel Libraryが良く、より高いレベルのソリューションを持っているかもしれないということかもしれません

using System; 
using System.Threading; 
using System.Threading.Tasks; 

namespace Demo 
{ 
    class Program 
    { 
     const int TASK_COUNT = 3; 
     static readonly Barrier barrier = new Barrier(TASK_COUNT); 
     static readonly AutoResetEvent gate = new AutoResetEvent(true); 

     static void Main() 
     { 
      Parallel.Invoke(task, task, task); 
     } 

     static void task() 
     { 
      while (true) 
      { 
       Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " is waiting at the gate."); 

       // This bool is just for test purposes to prevent the same thread from doing the 
       // work every time! 

       bool didWork = false; 

       if (gate.WaitOne(0)) 
       { 
        work(); 
        didWork = true; 
        gate.Set(); 
       } 

       Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " is waiting at the barrier."); 
       barrier.SignalAndWait(); 

       if (didWork) 
        Thread.Sleep(10); // Give a different thread a chance to get past the gate! 
      } 
     } 

     static void work() 
     { 
      Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " is entering work()"); 
      Thread.Sleep(3000); 
      Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " is leaving work()"); 
     } 
    } 
} 

:ここ

は、いくつかのサンプルコードです。それはちょっと読んで読む価値があります。

0

まず、待っているスレッドは何もしません。待つだけです。イベントからの信号を受け取った後、単にメソッドから抜けるので、whileループを追加する必要があります。その後、@ MatthewWatsonが提案したように、マニュアルの代わりにAutoResetEventを使用することができます。また、軽量ソリューションであるループ内にSpinWaitを含めることもできます。

第2に、flagフィールドの場合、これは間違いなくboolの場合はintを使用してください。

第3に、なぜ@grrrrrrrrrrrrが示唆するように、単純なロックを使用しないでください。これはまさにあなたがここでやっていることです。他のスレッドを強制的に待ちます。コードがwriteのもので、特定の時間に1つのスレッドでしかなく、複数のスレッドでreadにできる場合は、そのような同期にReaderWriterLockSlimオブジェクトを使用できます。

0

私が達成したいのは、より高速な実行、低いオーバーヘッドときれいな外観です。

速く実行

あなたの「DOスタッフは」非常に高速でない限り、このコードはすべての主要なオーバーヘッドを持つべきではありません。

下部再び

オーバーヘッド手動リセットイベントがあるように、インターロックExchangeは、/ CompareExchangeは、非常に低いオーバーヘッドです。

「お手伝い」がの場合、実際にはとなります。リンクリストのヘッドを移動させる、あなたがスピンすることができます:

きれいな外観シングルスレッドのC#のコードを修正するために比較したときに

正しいマルチスレッドのC#コードはほとんどきれいに見えるん。言語のイディオムはまだそこにはありません。

と言っています。が本当にの速い操作(「数十サイクル」)を持っていれば、あなたは回ることができます:(あなたのコードが何をしているのかはっきりとは分かりませんが、正しい)。

if (Interlocked.CompareExchange(ref _flag, 1, 0) == 0) 
     { 
      try 
      { 
       // do stuff that is very quick. 
      } 
      finally 
      { 
       Interlocked.Exchange(ref _flag, 0); 
      } 
     } 
     else 
     { 
      SpinWait.SpinUntil(() => _flag == 0); 
     }