2012-04-01 10 views
1

私はInterlocked.CompareExchangeを使用する方が高速であることを知っていますが、ロックに制限されている場合は最良の方法を探しています。ロックを使用して、以前の値に基づいて2つの関連フィールドを変更する最も効率的な方法はありますか?

Joe Albahari says the following:

それは安全に(例えば、それが「増加」にPERCENTCOMPLETE値が可能です)、その前回値に基づいて新しいProgressStatusオブジェクトを割り当てることも可能です - コードの複数行にわたってロックせず。

編集:@ drchの回答に基づいて更新されたコード。小さなより効率的なロックでこれを行う方法があるのを待っています。このロックは、読み込み、書き込み、および新しいコンストラクタをすべてロック内に持ちます。より小さな\より効率的なロックを望んでいた。ありがとう。

class ProgressStatus 
{ 
    public readonly int PercentComplete; 
    public readonly string StatusMessage; 
    public ProgressStatus(int percentComplete, string statusMessage) 
    { 
     PercentComplete = percentComplete; 
     StatusMessage = statusMessage; 
    } 
} 
class Test 
{ 
    readonly object _statusLocker = new object(); 
    ProgressStatus _status; 
    void IncreaseProgress() 
    { 
     lock (_statusLocker) 
      _status = new ProgressStatus(_status.PercentComplete + 10, _status.StatusMessage); 
    } 
} 
+1

IncreaseProgressのみ、正しいつのスレッドによって実行されますか? 2つのスレッドが同じstatusCopyをキャッシュすると、複数のスレッドがそれを実行していると、更新が失われる可能性があるからです。 – aKzenT

+0

@ aKzenTいいえこれは、多くのスレッドがIncreaseProgressを一度に呼び出すと仮定しています。コードの欠陥を指摘してくれてありがとう、これは私がジョーAlbahariが話している1つのライナーを探している理由です。 –

+0

http://msdn.microsoft.com/en-us/library/dd78zt0c.aspxを見ましたか? –

答えて

1

上記の記事では、1行でロックするだけで、後でPart 5という高度なテクニックが参照されていました。

上記のコードに関しては、2つのロックの間で競合状態が発生する可能性があります。 2つのスレッドは同じ値を読み取ってから、両方のインクリメントを必要とする場合に同じ値を書き込むことができます。

ロック全体の読み取りと書き込みオーバー:

lock (_statusLocker) { 
    statusCopy = _status; 
    var newStatus = new ProgressStatus(statusCopy.PercentComplete + 10, statusCopy.StatusMessage); 
    _status = newStatus; 
} 

それとも単に:

lock (_statusLocker) { 
    _status = new ProgressStatus(_status.PercentComplete + 10, _status.StatusMessage); 
} 
+0

ロックの中に「新しい」ことをすることは、それが実践にはあまり適していないという印象を受けました。 +1を1行にするのは+1ですが、ロック内に「新規」がなければこれを行う方法があるのか​​どうか不思議です。ありがとう。 –

+0

「新」は悪い習慣ではありません。新しいものはますます高速になります。昨日、私はGCを含むCPUコアごとに100万分の1秒の新しいことができることをベンチマークしました。それについて考えないでください。 – usr

+1

いずれにせよ、性能に対する正確性。あなたが何をしているのか分からない限り、スレッドを巧みにしないでください。 – usr

0

IMOでは、上記のコードを使用して、ロックなしで行うことができます。これは、不変クラスを使用し、データのコピーのみを使用しているためです。読み取りと書き込みの参照は、C#仕様によってアトミックになることが保証されています。

編集:しかし、スレッドの同期がABは***ですので、私はいくつかのより多くの意見を聞きたいと思います...

編集:私はコメントで指摘したようにはまた、欠陥がありますコード内に複数のスレッドがメソッドを呼び出す場合この場合、更新が失われる可能性があります。

+2

あなたは 'Interlocked'を使ってのみロックを避けることができ、OPは明示的に彼がそれをしたくないと言った。 – CodesInChaos

+0

私が言いたいのは、ロックを使わなくても、ProgressStatusは常に良い状態にあり、誰かが進捗状況を読むために汚れた値や奇妙な振る舞いがないということです。しかしコメントで指摘されているように、彼らは進行状況の更新を失うかもしれないが、ロックはそれを避けるために何もしなかった。元のコードでは、ロックなしではロックなしの場合と同じ(間違った)効果を得ることができませんでした。 – aKzenT

関連する問題