최악 (실제로는 작동하지 않음)
의 액세스 한정자 변경 counter
에를public volatile
다른 사람들이 언급했듯이, 이것 자체는 실제로 안전하지 않습니다. 요점은 volatile
여러 CPU에서 실행되는 여러 스레드가 데이터를 캐시하고 명령을 다시 정렬 할 수 있다는 것입니다.
이 값이 아니고 volatile
CPU A가 값을 증가시키는 경우, CPU B는 시간이 지나야 실제로 증가 된 값을 보지 못할 수 있으며, 이로 인해 문제가 발생할 수 있습니다.
이 경우 volatile
두 CPU가 동일한 데이터를 동시에 볼 수 있습니다. 그것은 당신이 피하려고하는 문제 인 읽기 및 쓰기 작업을 인터리브하는 것을 전혀 막지 않습니다.
두번째로 좋은:
lock(this.locker) this.counter++
;
이 작업은 안전합니다 ( lock
액세스하는 다른 곳 을 기억하는 경우 this.counter
). 다른 스레드가에 의해 보호되는 다른 코드를 실행하지 못하게합니다 locker
. 잠금을 사용하면 위와 같이 다중 CPU 재정렬 문제를 방지 할 수 있습니다.
문제는 잠금이 느리고 locker
실제로 관련되지 않은 다른 장소에서 재사용 하면 이유없이 다른 스레드를 차단할 수 있다는 것입니다.
베스트
Interlocked.Increment(ref this.counter);
이는 중단 될 수없는 '한 번의 히트'에서 읽기, 증분 및 쓰기를 효과적으로 수행하므로 안전합니다. 이로 인해 다른 코드에는 영향을 미치지 않으며 다른 곳에서도 잠글 필요가 없습니다. MSDN은 최신 CPU에서 말 그대로 단일 CPU 명령 일 때도 매우 빠릅니다.
그러나 다른 CPU가 물건을 재정렬하거나 휘발성을 증가와 결합 해야하는지 확실하지 않습니다.
연동 노트
- 인터 로크 된 방법은 많은 수의 코어 또는 CPU에서 동시에 안전합니다.
- 인터 로킹 된 메소드는 실행되는 명령어를 완전히 차단하므로 재정렬이 발생하지 않습니다.
- 휘발성은 주어진 필드에서 작업 주위에 절반 펜스를 배치하고 인터록은 전체 펜스를 사용하므로 인터록 된 메소드 는 휘발성 필드에 대한 액세스를 지원하지 않아도 됩니다.
각주 : 실제로 휘발성이 좋은 것.
으로 volatile
그것을 무엇을, 멀티 스레딩 이러한 종류의 문제를 방지하지 않는 이유는 무엇입니까? 좋은 예는 두 개의 스레드가 있는데, 하나는 항상 변수에 쓰는 스레드 queueLength
와 같은 변수에서 항상 읽는 스레드 입니다.
queueLength
휘발성이 아닌 경우 스레드 A는 다섯 번 쓸 수 있지만 스레드 B는 이러한 쓰기가 지연된 것으로 볼 수도 있습니다 (또는 잠재적으로 잘못된 순서 일 수도 있음).
해결책은 잠그는 것이지만이 상황에서 휘발성을 사용할 수도 있습니다. 이렇게하면 스레드 B가 스레드 A가 작성한 최신 정보를 항상 볼 수 있습니다. 참고 그러나이 논리는 단지 당신이 쓰는 결코 읽어 본 적이 작가와 독자가 있다면, 작동 및 것은 당신이있는 거 쓰기가 원자 값 인 경우. 단일 읽기-수정-쓰기를 수행하자마자 연동 작업으로 이동하거나 잠금을 사용해야합니다.