2010-01-28 3 views
11

나는 Concurrent Programming에 대한 Joe Duffy의 책을 읽었습니다. 나는 lockless threading에 관한 학문적 질문을 가지고있다. 먼저자주 변경되지 않는 변수에 대해 잠금을 사용하지 않아도됩니까?

가 : 내가이 있다고 가정 : 나는 그럼에도 불구하고

(당신이 저를 믿지 않는 경우, 메모리 모델에 대한 책에 섹션을 읽기) 잠금없는 스레딩은 위험이 따른다는 것을 알고, 나는 질문이 있습니다 클래스에 int 속성이 있습니다.

이 속성에 의해 참조 값

값이 변경됩니다 매우 드문

여러 스레드에 의해 매우 자주 읽을 수 있습니다, 그리고 그렇게되면 그것을 변경하는 단일 스레드 될 것입니다. 이 비행에 사용하는 다른 작업이 아무도 손가락을 잃게 될하지 않습니다 동안 변경할 경우

내가 잠금을 사용할 수 있습니다 (이 수행하여 가장 먼저 누군가는 지역 변수에 복사입니다) (또는 동시 읽기를 유지하기 위해 readerwriterlockslim). 휘발성 변수를 표시 할 수 있습니다 (많은 경우 예제가 있습니다)

그러나 휘발성으로 인해 성능이 저하 될 수 있습니다.

변경시 VolatileWrite를 사용하고 읽음에 대한 액세스를 정상적으로 유지하면 어떨까요? 이런 식으로 뭔가 :

public class MyClass 
{ 
    private int _TheProperty; 
    internal int TheProperty 
    { 
    get { return _TheProperty; } 
    set { System.Threading.Thread.VolatileWrite(ref _TheProperty, value); } 
    } 
} 

나는 내가 실제 생활에서 이것을 시도 것이라고 생각하지 않는다,하지만 난 메모리 모델 물건을 이해 여부의 체크 포인트로, 무엇보다 (답에 대해 궁금 해요 나는 읽고 있었다).

답변

6

변수에 "휘발성"으로 표시하는 것은 두 가지 효과가 있습니다.

1) 읽기 및 쓰기가 의미를 확보하고 해제하므로 다른 메모리 위치의 읽기 및 쓰기가이 메모리 위치의 읽기 및 쓰기와 관련하여 "시간상 및 시간상으로 이동하지"않습니다. (이것은 단순화이지만 내 요점을 이해합니다.) 2) 지터에 의해 생성 된 코드는 논리적으로 변함없는 값을 "캐시"하지 않습니다.

이전 지점이 귀하의 시나리오와 관련이 있는지 여부는 모르겠습니다. 당신은 단지 하나의 메모리 위치만을 설명했습니다. 휘발성 글쓰기 만 있지만 휘발성 읽기가 아닌지 여부가 중요한지 여부는 결정할 사항에 달려 있습니다.

그러나 나에게있어 후자의 점은 매우 적합합니다. 당신은 비 휘발성 변수에 스핀 잠금 장치가있는 경우 : 당신이

if (this.prop == 0) { while (true) {} } 

을 작성했던 것처럼

while(this.prop == 0) {} 
지터가 자신의 권리 내

는 실제로 그렇게하지 않거나 상관없이이 코드를 생성하는 , 나도 몰라,하지만 권리가있다. 루프가 루프를 돌 때마다 속성을 실제로 다시 검사하도록 코드가 원하는 경우이를 휘발성으로 표시하는 것이 올바른 방법입니다.

+0

에릭에게 감사드립니다. 그리고 저는 동의합니다. 두 번째 점은 잠금 장치가 필요하다면 올바르게하기가 얼마나 까다로운지를 증명합니다. 이것은 정말로 나를위한 학문적 인 운동이었습니다 - 저는 잠금 장치가없는 것을 피했습니다. 그러나, 뒤늦은 생각으로, 나는 이것이 정말로 가치있는 대화 였다고 생각합니다. 때때로 나는 왜 우리가 한 가지 또는 다른 것에 대해 잠글 수 없어야하는지/묻지 않았습니다. 나는 보통 "너 xxx를 생각해 봤어?" 이 대화 상자는 잠김없는 토론이 올 때 내 사례에 대한 더 많은 증거를 제공했습니다. 다들 감사 해요. – JMarsch

0

C#에서 int 유형은 스레드로부터 안전합니다.

하나의 스레드 만 쓰고 있다고 말했기 때문에 적절한 값이 무엇인지에 대해 논쟁해서는 안되며 로컬 복사본을 캐싱하는 한 결코 더러운 데이터를 가져 오지 않아야합니다.

그러나 OS 스레드가 업데이트를 수행하는 경우 volatile으로 선언 할 수 있습니다.

또한 일부 작업원자없는 명심하고, 하나 명 이상의 작가가있는 경우 문제가 발생할 수 있습니다.

a = !a; 

원자되지 않습니다 : 심지어 bool 유형 늘 손상하지만 예를 들어, 하나 명 이상의 작가,이 같은 문이있는 경우. 두 스레드가 동시에 읽으면 경합 상태가됩니다.

+4

"한번도 쓰지 않은 값을 보지 못함"과 "항상 최신 값을보고있는 것"사이에는 차이가 있습니다. 메모리 장벽이 없어도 항상 이전 값을 볼 수 있습니다. –

+0

@ 존 : 그건 단지 * 당신이 휘발성이라고 선언한다면? –

+0

@ 존 : 아니요. 휘발성의 문제는 그 문제에서 벗어나려고 노력하는 것입니다. –

1

이것은 '마지막 작가 승'패턴이 상황에 적용될 때 사용하는 패턴입니다. volatile 키워드를 사용했지만 Jeffery Richter의 코드 예제에서이 패턴을보고 나서 필자는이 패턴을 사용하기 시작했습니다.

+0

흥미 롭습니다. 어떤 리히터 북에서 그 내용을 다루었습니까? – JMarsch

+0

2007 년 Devscovery Redmond에서 그를 물었습니다. 저는 C#을 통해 CLR에도 있다고 생각했습니다. – codekaizen

4

질문은 읽을 스레드가 인 지 여부는입니다. 즉시이 표시되는지 여부 만 문제가 아닙니다.

솔직하게 나는 변동성을 이해하려고 애를 썼다. 나는 그것이 내가 생각했던 것만 큼 의미하지는 않는다는 것을 알고있다. 그러나 나는 또한 독서 스레드에 어떠한 종류의 메모리 장벽도 없다는 것을 안다. 동일한 오래된 데이터를 영원히 읽을 수 있습니다. 컴파일러는 지금 실제로 멀리 최적화하는 대신 값을 확인하는 코드를 생성하기 때문에

+0

나는 그것을 이해하는 것을 포기하는 것에 동의한다! 그래서 나는 이것을 빈혈에 관한 질문으로 생각한다. "언제나"볼 수 있을지는 묻습니다. 질문을하게 된 것은 정확히 무엇입니까? 나는 즉시 걱정할 필요가 없습니다. 자물쇠를 사용하더라도, 여전히 경주가 있습니다 (작가는 작가가하기 전에 자물쇠를 얻을 것입니다). Joe의 블로그를 꽤 많이 읽었습니다. 변동성 문제가 발생할 때마다 생각한 것 같아서 생각보다 더 이상한 새로운 소식을 읽었습니다. 어쨌든, 당신의 생각을 게시 주셔서 감사합니다! – JMarsch

+0

@Jon 빠른 질문 : 메모리 장벽으로 인해 명령의 순서가 변경되는 것을 방지 할 수 없으며 휘발성 읽기/쓰기로 인해 캐시 플러시가 발생합니까? 장벽이 플러시를 의미합니까? 또는 일시적인 읽기/쓰기가 장벽을 의미합니까? (또는 둘 다?) 아아. – JMarsch

+0

@JMarsch : 메모리 장벽은 명령에 의해 요구되는 * 메모리 액세스 *시에 명령을 부과합니다. "명령의 순서 변경"을 막을 지 여부는 프로세서의 구현 세부 사항입니다. 메모리 장벽은 특정 작업의 관찰 가능한 결과에 대한 보증을 제공하기 만합니다. –

2

volatile의 "성능 저하는"- 즉, 당신이 에 관계없이 당신이 무엇의 공격 그 성능을 취할을해야합니다.

+1

휘발성의 유일한 성능은 아닙니다. 더 관련성이있는 것은 변동성이 캐시가 플러시되는 방식을 변경하는 방법입니다. –

+0

@Anon 내가 탐구하는 차이점은 다음과 같습니다. volatile 키워드를 사용하면 읽기 및 쓰기가 일시적으로 변합니다. (이 사고 실험에서, 많은 읽기가 있고, 거의 쓰지 않을 것입니다). 따라서 휘발성 쓰기는 할 수 있지만 최적화는 할 수없는 최적화가 가능한지 검토 중입니다. – JMarsch

+0

@ 에릭 : 나는 동의한다 - 그것이 내가 궁금한 것이지. 오늘날, 그것은 나에게 학문적 인 것이지만, 이해할 때, 코어의 수 (그리고 그에 따라 동기화 할 캐시)가 증가함에 따라 패널티가 커질 것입니다. (조는 그의 책에서 그것을 언급했다). 따라서 오늘날 4 코어가 표준이지만 64 코어 또는 128 코어가 표준/표준 코어 인 경우 어떤 종류의 문제가 발생합니까? – JMarsch

1

메모리 매핑 장치와 같은 정상적인 경우 CPU/CPU 내에서/CPU간에 진행되는 캐시 일관성 프로토콜을 통해 해당 메모리를 공유하는 다른 스레드가 일관성있는보기를 확보 할 수 있습니다 (즉, 하나의 CPU에서 메모리 위치의 값을 변경하면 이 캐시에 메모리가있는 다른 CPU에서 볼 수 있습니다. 이와 관련하여 휘발성 (volatile)은 옵티마이 저가 레지스터에 캐시 된 값을 읽음으로써 메모리 접근을 최적화하지 않는다는 것을 보장합니다. C# 문서는이 점에서 매우 분명해 보입니다. 다시 말하지만, 애플리케이션 프로그래머는 일반적으로 캐시 일관성을 처리 할 필요가 없습니다.

자유롭게 사용할 수있는 논문 "모든 프로그래머가 메모리에 대해 알아야 할 내용"을 읽는 것이 좋습니다. 대부분의 마술은 두건 아래에서 발에 총을 발사하지 못하게합니다.

2

CPU 레벨에서 모든 프로세서가 예 으로 변경됩니다. 메모리 주소가 변경된 것을 확인하십시오. 잠금 장치 나 메모리 장벽이 없어도. 자물쇠와 장벽은 상대 순서 (다른 지침과 함께)로 모든 것이 발생하여 프로그램에 올바르게 나타나는지 확인합니다.

캐시 일관성 문제가 아닙니다. Joe Duffy의 저서는 실수가 아닙니다. 캐시는 일관된 상태를 유지합니다. 단지 시간이 걸리는 것 뿐이며, 프로세서가이를 시행하지 않는 한 프로세서가 기다리지 않아도됩니다.그래서 프로세서는 다음 명령으로 이동합니다. 이전에 이전에 앞에 있습니다 (각 메모리 읽기/쓰기는 다른 양의 시간이 걸리기 때문에) 왜냐하면은 프로세서는 일관성 등에 동의해야합니다. 이렇게하면 일부 캐시 라인이 다른 캐시 라인보다 더 빨리 처리됩니다 (즉, 수정 된 라인, 배타적 인 스프레드, 공유 된 스프레드 시트 또는 유효하지 않은 스프레드 시트에 따라 필요한 상태가되기까지 더 많은 작업을 수행함).

읽기가 오래된 캐시 나 오래된 캐시에서 나타날 수 있지만 실제로는 예상보다 일찍 발생했습니다 (일반적으로 미리보기 및 분기 예측 때문). 실제로 으로 읽혀지면 캐시는 일관성이 있었고, 그 이후로 변경되었습니다. 따라서 값을 읽으면서 값이 늙지는 않았지만 지금은 필요할 때가되었습니다. 방금 너무 빨리 읽었습니다. :-(

또는 동등하게, 그것은 코드의 논리 이후에 쓰여진는 기록 될 거라 생각 했어요.

또는 둘 모두를. ​​

어쨌든,이 C/C++ 인 경우에도 잠금 장치없이/장벽, 당신은 결국 업데이트 된 값을 얻을 것이다. (일반적으로 메모리가 오래 걸립니다) .C/C++에서 휘발성 (약한 비 스레드 비 휘발성)을 사용하여 값 wasn 레지스터에서 읽지 마라. (이제는 비 일관된 캐시가있다.)

C#에서는 값이 얼마나 오래 레지스터에 머무를 수 있는지, 또는 메모리에서 실제 다시 읽음을 얻는 방법을 알기 위해 CLR에 대해 충분히 알지 못합니다. '약한'휘발성을 잃었습니다.

가변 액세스가 완전히 컴파일되지 않는 한, 궁극적으로 레지스터가 부족해집니다 (x86에는 시작해야 할 것이 많지 않음). 그러면 다시 읽을 수 있습니다.

내가 볼 수있는 보장은 없습니다. 휘발성 읽기를 코드의 특정 지점으로 제한 할 수 있다면 (종종 다음 작업 (예 : things_to_do) 루프가 시작되는 경우) 너무 자주는 아니지만 그렇게 할 수있는 최선의 방법 일 수 있습니다.

+0

나는 캐시 일관성에 동의하지만 엄격하게 동의하지 않는 프로세서에 관해서는 당신에게 엄격하게 동의하지는 않습니다. 대부분의 하드웨어는 CPU가 스누핑 (snooping)을 구현하여 CPU가 저장소 버퍼와 캐시를 모두 검사하고 저장소 버퍼가 최신 인 경우 잘못된 캐시 된 값을 무시합니다. – zebrabox

+0

예를 들어 CPU가 읽기 요청을 발행 한 다음 다음 명령어 (읽기에 아무런 의존성이 없다면 이것이 앞을 내다보고 일찌감치 읽는다). 그리고 쓰기를위한 moreso. 쓰기를 실행하지만 완료 될 때까지 기다리지 마십시오. 무서운 것 하나는 메시지 무효화입니다. CPU가 캐시 라인이 유효하지 않다고 말하며, 여러분의 말을 듣고 있다고 말하지만, 그것이 저에게 singlethreadedly 영향을 미치지 않으면 임시로 무시할 것입니다.알파가 p를 읽기 전에 * p '를 읽는 방법입니다. – tony

+0

Tony에게 감사 드리며 John의 게시물에 대한 귀하의 추가 의견에 감사드립니다. – JMarsch

관련 문제