2011-01-23 5 views
5

私は、URLのリストを取り、それぞれの処理を追加するParallel.ForEach()ループを持っています。私のループの外側では、ループカウンタ変数を宣言しています。ループ本体の内部では、Interlocked.Increment()を使用すると、各スレッドのインターラクションが実行されるたびにカウントを増加させる「スレッドセーフな」方法を維持する最善の方法と考えると思います。私は私が何かに似て見るだろうと思っているだろうInterlocked.Increment()は、私がタスク並列ライブラリで期待していた方法では動作しません。

int counter = 0; 

Parallel.ForEach(urlList, (url, state) => 
{ 
    // various code statments 

    Interlocked.Increment(ref counter); 

    Debug.WriteLine(" ......... counter: " + counter); 
}); 

: "......... 0" を

......... 1 
......... 2 
......... 3 
......... 4 
......... 5 
......... 
......... 
......... n 

をしかし、私が代わりに取得することは16である(これが原因であるI 8つのネイティブコアを持つデュアルクアッドコアコンピュータがありますが、ハイパースレッディングが有効になり、合計で16コアになります)。次に、カウンタが通常ほとんど増分されるのを見始めますが、デバッグ出力にカウンタ値が重複していたり​​、3倍になったりすることがあります。

Parallel.ForEach()を使用すると、ループ反復回数をカウントする最も良い方法は何ですか?アドバイスありがとう。

+0

重複した値が予想されます。しかし、私は0がどこから来たのか分かりません。 – CodesInChaos

+0

回避策は、int myCounter = Interloced.Increment(refカウンタ)を使用しています。 Debug.WriteLine(myCounter); ' – CodesInChaos

+3

実際にコンパイルして問題を示すコードを投稿することはできますか(つまり、prints 0s)? – CodesInChaos

答えて

12

Interlocked.Incrementはあなたの増分値を返します。

それがインクリメントされたように、

int counter = 0; 

Parallel.ForEach(urlList, (url, state) => 
{ 
    // various code statments 

    var counterNow = Interloced.Increment(ref counter); 

    Debug.WriteLine(" ......... counter: " + counterNow); 
}); 

は、カウンタ値を返す必要があります。かなりの時間後に

編集:説明の仕方によって

あなたは、マルチプロセッサ/マルチコアマシン上で実行されていると複数のアプリケーションスレッドを持っているとき、あなたは注意する必要があります変数に表示される値がその変数の実際の現在の状態を反映していない可能性があります。 これは、各CPUまたはダイまたはソケットごとに多数の個別キャッシュが存在する可能性があり、スレッドがキャッシュされた値を読み取ることができる(ヒットをメインメモリに読み込む)

値複数のスレッドで更新された場合、counterの現在の値を保証するには、Interlocked.Read()を使用する必要があります。

+0

それはトリックでした。 Hughes氏とCodeInChaosのおかげで同じ提案がありました。 – BonanzaDriver

7

これは、Increment + WriteLineがともにアトミックではないためです。
thread1がcounterをインクリメントした後、thread2が再びインクリメントし、2つのスレッドが同じ値のcounterを持つWriteLine部分に到達することがあります。

+1

しかし、なぜ「カウンタ」は「0」と書かれていますか? 'WriteLine()'は、少なくとも1つの 'Interlocked.Increment()'呼び出しの後にのみ発生します。 –

+0

カウンタをvolatileとして宣言しようとします。 –

+1

インターロックは既にmem-barrierを意味するので、volatileは違いを生むべきではありません。 –

1

私は確信していませんが、これはおそらく可変キャプチャがlambdaで動作するためです。別の関数に入れたり、変数を外部に移動して静的変数として宣言したりしてみましたか?

関連する問題