내 다중 스레드 asmx 웹 서비스에는 몇 개로 구성 List<T>
되고으로 Dictionary<T>
표시된 SystemData 유형의 클래스 필드 _allData가 volatile
있습니다. 시스템 데이터 ( _allData
)는 가끔 새로 고침되며라는 다른 객체를 만들고 newData
새 데이터로 데이터 구조를 채 웁니다. 완료되면 할당합니다.
private static volatile SystemData _allData
public static bool LoadAllSystemData()
{
SystemData newData = new SystemData();
/* fill newData with up-to-date data*/
...
_allData = newData.
}
이것은 할당이 원자적이고 이전 데이터에 대한 참조가있는 스레드가 계속 사용하고 나머지는 할당 직후 새 시스템 데이터를 가지고 있기 때문에 작동합니다. 그러나 제 동료는 일부 플랫폼에서 참조 할당이 원자 적이라는 보장이 없기 때문에 volatile
키워드와 간단한 할당 을 사용하는 대신 사용해야 InterLocked.Exchange
한다고 말했습니다. 또한 : 때 선언 the _allData
으로 필드를volatile
Interlocked.Exchange<SystemData>(ref _allData, newData);
“휘발성 필드에 대한 참조는 휘발성으로 처리되지 않습니다”라는 경고를 생성합니다. 이에 대해 어떻게 생각해야합니까?
답변
여기에 많은 질문이 있습니다. 한 번에 하나씩 고려 :
참조 할당은 원자 적이므로 Interlocked.Exchange (ref Object, Object)가 필요한 이유는 무엇입니까?
참조 할당은 원자 적입니다. Interlocked.Exchange는 할당을 참조 만하는 것이 아닙니다. 변수의 현재 값을 읽고 이전 값을 숨기고 새 값을 변수에 할당합니다.이 모든 것이 원자 적 연산입니다.
제 동료는 일부 플랫폼에서 참조 할당이 원자 적이라는 것이 보장되지 않는다고 말했습니다. 제 동료가 맞습니까?
아니요. 참조 할당은 모든 .NET 플랫폼에서 원 자성이 보장됩니다.
제 동료는 거짓 전제에서 추론하고 있습니다. 그것은 그들의 결론이 틀렸다는 것을 의미합니까?
반드시 그런 것은 아닙니다. 동료가 나쁜 이유로 좋은 조언을 줄 수 있습니다. Interlocked.Exchange를 사용해야하는 다른 이유가있을 수 있습니다. 잠금없는 프로그래밍은 엄청나게 어렵고 현장 전문가가지지하는 잘 확립 된 관행에서 벗어나는 순간 잡초에 빠져 최악의 경쟁 조건에 처하게됩니다. 저는이 분야의 전문가도 아니고 귀하의 코드에 대한 전문가도 아니므로 어떤 식 으로든 판단을 내릴 수 없습니다.
“휘발성 필드에 대한 참조는 휘발성으로 처리되지 않습니다”라는 경고를 생성합니다. 이에 대해 어떻게 생각해야합니까?
이것이 일반적인 문제인 이유를 이해해야합니다. 그러면이 특정 경우에 경고가 중요하지 않은 이유를 이해할 수 있습니다.
컴파일러가이 경고를 표시하는 이유는 필드를 휘발성으로 표시한다는 것은 “이 필드가 여러 스레드에서 업데이트 될 것입니다.이 필드의 값을 캐시하는 코드를 생성하지 말고 모든 읽기 또는 쓰기를 확인하십시오. 이 필드는 프로세서 캐시 불일치를 통해 “시간상 앞뒤로 이동”되지 않습니다.
(이미 모든 것을 이해하고 있다고 가정합니다. 휘발성의 의미와 프로세서 캐시 시맨틱에 미치는 영향에 대해 자세히 이해하지 못한 경우 작동 방식을 이해하지 못하고 휘발성을 사용해서는 안됩니다. 잠금없는 프로그램 프로그램이 올바른지 확인하기가 매우 어렵습니다. 프로그램이 작동하는 방식을 이해하고 있기 때문에 우연히 옳지 않은지 확인하십시오.)
이제 해당 필드에 ref를 전달하여 휘발성 필드의 별칭 인 변수를 만든다고 가정합니다. 호출 된 메서드 내에서 컴파일러는 참조가 휘발성 의미를 가져야한다는 것을 알 이유가 전혀 없습니다! 컴파일러는 휘발성 필드에 대한 규칙을 구현하지 못하는 메서드에 대한 코드를 쾌활하게 생성하지만 변수 는 휘발성 필드입니다. 그것은 잠금없는 논리를 완전히 망칠 수 있습니다. 휘발성 필드는 항상 휘발성 의미론으로 액세스 된다는 가정이 항상 있습니다. 때로는 그것을 휘발성으로 취급하고 다른 시간에는 취급하지 않습니다. 항상 일관성 이 있어야합니다. 그렇지 않으면 다른 액세스에 대한 일관성을 보장 할 수 없습니다.
따라서 컴파일러는 신중하게 개발 한 잠금없는 논리를 완전히 엉망으로 만들 것이기 때문에이 작업을 수행 할 때 경고를 표시합니다.
물론 Interlocked.Exchange 는 휘발성 필드를 예상하고 올바른 작업을 수행하도록 작성되었습니다. 따라서 경고는 오해의 소지가 있습니다. 나는 이것을 매우 후회한다. 우리가해야 할 일은 Interlocked.Exchange와 같은 메서드의 작성자가 “참조를 취하는이 메서드는 변수에 휘발성 의미를 적용하므로 경고를 억제”라는 속성을 메서드에 넣을 수있는 메커니즘을 구현하는 것입니다. 아마도 향후 버전의 컴파일러에서 그렇게 할 것입니다.
답변
동료가 착각했거나 C # 언어 사양에없는 것을 알고 있습니다.
“다음 데이터 유형의 읽기 및 쓰기는 원자 적입니다 : bool, char, byte, sbyte, short, ushort, uint, int, float 및 reference 유형.”
따라서 손상된 값을 얻을 위험없이 휘발성 참조에 쓸 수 있습니다.
물론 한 번에 둘 이상의 스레드가 수행하는 위험을 최소화하기 위해 새 데이터를 가져올 스레드를 결정하는 방법에주의해야합니다.
답변
원자 단위 연산으로 지정된 유형 T의 변수를 지정된 값으로 설정하고 원래 값을 반환합니다.
그것은 변경하고 원래의 값을 반환합니다. 그것은 당신이 그것을 바꾸고 싶기 때문에 쓸모가 없습니다. 그리고 Guffa가 말했듯이 그것은 이미 원자 적입니다.
프로파일 러가 애플리케이션의 병목 현상으로 입증되지 않는 한 잠금 해제를 고려해야합니다. 코드가 옳다는 것을 이해하고 증명하는 것이 더 쉽습니다.
답변
Iterlocked.Exchange()
원 자성뿐만 아니라 메모리 가시성도 처리합니다.
다음 동기화 기능은 적절한 장벽을 사용하여 메모리 순서를 보장합니다.
중요한 섹션에 들어가거나 나가는 기능
동기화 객체를 신호하는 기능
대기 기능
연동 기능
이는 원 자성 외에도 다음을 보장한다는 것을 의미합니다.
- 그것을 호출하는 스레드의 경우 :
- (컴파일러, 런타임 또는 하드웨어에 의해) 명령어의 재정렬이 수행되지 않습니다.
- 모든 스레드 :
- 이 명령어 이전에 발생한 메모리 읽기는이 명령어가 변경 한 내용을 볼 수 없습니다.
- 이 명령어 이후의 모든 읽기에는이 명령어로 변경된 내용이 표시됩니다.
- 이 명령어 이후의 메모리에 대한 모든 쓰기는이 명령어 변경이 메인 메모리에 도달 한 후에 발생합니다 (이 명령어 변경이 완료되면 메인 메모리로 플러시하고 하드웨어가 자신의 온 타이밍을 플러시하지 않도록 함).