2012-03-01 10 views
7

私はスレッドセーフなメソッドを記述しようとしていますが、これは一度だけ(オブジェクトインスタンスごとに)呼び出すことができます。以前に呼び出された場合は、例外をスローする必要があります。方法:一度だけ呼び出すことができるスレッドセーフメソッドを記述しますか?

私は2つの解決策を考え出しました。彼らはどちらも正しいですか?そうでない場合、何が問題なのですか? lock

  1. public void Foo() 
    { 
        lock (fooLock) 
        { 
         if (fooCalled) throw new InvalidOperationException(); 
         fooCalled = true; 
        } 
        … 
    } 
    private object fooLock = new object(); 
    private bool fooCalled; 
    
  2. Interlocked.CompareExchange付:私は間違っていない場合は、このソリューションはロックフリーであるという利点を有する(

    public void Foo() 
    { 
        if (Interlocked.CompareExchange(ref fooCalled, 1, 0) == 1) 
         throw new InvalidOperationException(); 
        … 
    } 
    private int fooCalled; 
    

    私の場合は無関係と思われる)、それよりも少ないプライベートフィールド。

私はまた、どちらの解決策を優先すべきかについての正当な意見や、より良い方法があればさらに提案することができます。

答えて

5

あなたのInterlocked.CompareExchangeソリューションが最もよく見え、(あなたが言ったように)ロックフリーです。他のソリューションよりも複雑さが大幅に軽減されています。ロックはかなり重いですが、CompareExchangeは1つのCAS cpu命令までコンパイルできます。私はそれと一緒に行くと言う。

+0

好奇心を要しています。「あまり複雑ではない」と言えば、ブラインドの背後にあるすべてのものを指しているようです。あなたは平均的なプログラマーのための 'Interlocked.ExchangeCompare'構造の読みやすさ/理解の容易性をどのように判断しますか? – stakx

+1

@stakx:これはコメントのためのものです。プログラマが理解できないことに遭遇すると、プログラマはそれを理解して理解するように見上げるべきです。それが彼らがより良いプログラマになる方法です。 – thecoop

+0

@thecoop私はそれに反してはい、それは最も正しい解決策ですが、それは単純ではありません、あなたは原子操作などについて知る必要があります。これは何とか初期化プロセスであり、広く使用されている初期化パターンに従うことをお勧めしますチェックされたロック)。また、これらのパターンは、スレッドを実行するときに簡単に発生する可能性のあるものを逃してしまうのを防ぎます。 – ntziolis

0

ダブルチェックをロックパターはあなたが後にしているものです。

これはあなたが後にしているものです。これらのような問題にさらされた場合、一般的に

class Foo 
{ 
    private object someLock = new object(); 
    private object someFlag = false; 


    void SomeMethod() 
    { 
    // to prevent locking on subsequent calls   
    if(someFlag) 
     throw new Exception(); 

    // to make sure only one thread can change the contents of someFlag    
    lock(someLock) 
    { 
     if(someFlag) 
     throw new Exception(); 

     someFlag = true;      
    } 

    //execute your code 
    } 
} 

試してみても続くようなpattersを知っています上記。
パターンをたどるとき、特にスレッディングに遭遇したときに、何かを逃してしまう可能性が低いので、これは認識しやすく、エラーを起こしにくいからです。
あなたの場合、最初のifは多くの意味を持ちませんが、実際のロジックを実行してフラグを設定したいことがよくあります。あなたが(おそらくかなり高価な)コードを実行している間は、2番目のスレッドがブロックされます。

第2のサンプルについて:
はい、正しいですが、それよりも複雑にしないでください。あなたは単純なロックを使用しないようにすべき非常に良い理由があります。このような状況では、何も達成せずにコードを複雑にします(Interlocked.CompareExchange()はあまり知られていません)この場合のメリット)。

+0

** 1 **これは 'someFlag'を非原子的に読み書きしているようです。これが正しいと確信していますか? ** 2。** 'Interlocked.CompareExchange'ベースのソリューションはどうですか? – stakx

+0

sryが最も重要な行を忘れた – ntziolis

+0

ええと...とにかく例外を投げるつもりなら、このような問題をロックしていますか? – stakx

-1
Task task = new Task((Action)(() => { Console.WriteLine("Called!"); })); 
    public void Foo() 
    { 
     task.Start(); 
    } 

    public void Bar() 
    { 
     Foo(); 
     Foo();//this line will throws different exceptions depends on 
       //whether task in progress or task has already been completed 
    }  
+1

申し訳ありませんが、これは質問に答えるものではありません。 (Btw。私はタスク並列ライブラリを認識していますが、どこのメソッドでも使用することはできません)。 – stakx

+1

あなたのケースに応じて固定 – pamidur

+0

@pamidurあなたは別の問題を解決しようとしています – ntziolis

関連する問題