2011-01-23 6 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); 
}); 
가 나는 비슷한 볼 것이라고 생각했을 것이다

:

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

하지만 제가 대신 얻는 것은 16 "......... 0"(이 내가 때문이다 듀얼 쿼드 코어 컴퓨터에 8 개의 기본 코어가 있지만 하이퍼 스레딩을 사용할 수 있으므로 총 16 개의 코어가 제공됩니다.) 그런 다음 카운터가 정상적으로 증가하는 것을 보게 될 것이지만 때로는 디버그 출력에 카운터 값이 중복되거나 심지어 세 개나 표시됩니다.

Parallel.ForEach()를 사용하면 루프 반복 횟수를 계산하는 가장 좋은 방법은 무엇입니까? 어떤 조언을 주셔서 감사합니다.

+0

중복 값이 ​​예상됩니다. 그러나 나는 0이 어디서 왔는지 이해하지 못합니다. – CodesInChaos

+0

해결 방법은 int 'myCounter = Interloced.Increment (ref 카운터)를 사용하고 있습니다. Debug.WriteLine (myCounter); ' – CodesInChaos

+3

문제를 실제로 컴파일하고 표시하는 코드 (예 : 0을 인쇄)를 게시 할 수 있습니까? – 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가 다시 증가시키고 두 개의 스레드가 동일한 값인 counter을 갖는 WriteLine 부분에 도달 할 수 있습니다.

+1

그러나 왜 '카운터'는 '0'으로 기록됩니까? 'WriteLine()'은 최소한 하나의'Interlocked.Increment()'호출 후에 만 ​​발생합니다. –

+0

카운터를 휘발성으로 선언하십시오. –

+1

연동은 이미 mem-barrier를 의미하므로 휘발성이 차이를 가져서는 안됩니다. –

1

확실치 않지만, 이것이 아마도 변수 캡처가 람다에서 작동하는 방식 때문일 것으로 생각됩니다. 별도의 함수에 넣거나 변수를 외부로 옮기고 정적으로 선언 했습니까?

관련 문제