나는 옵티마이 저가 isComplete변수 에 'volatile'키워드가 없다는 것에 속았다 고 생각 합니다.
물론 로컬 변수이기 때문에 추가 할 수 없습니다. 그리고 물론, 그것은 지역 변수이기 때문에 전혀 필요하지 않아야합니다. 지역 변수는 스택에 유지되고 자연스럽게 항상 "신선"하기 때문입니다.
그러나 컴파일 후에는 더 이상 로컬 변수 가 아닙니다 . 익명 대리자에서 액세스되기 때문에 코드가 분할되고 다음과 같은 도우미 클래스 및 멤버 필드로 변환됩니다.
public static void Main(string[] args)
{
TheHelper hlp = new TheHelper();
var t = new Thread(hlp.Body);
t.Start();
Thread.Sleep(500);
hlp.isComplete = true;
t.Join();
Console.WriteLine("complete!");
}
private class TheHelper
{
public bool isComplete = false;
public void Body()
{
int i = 0;
while (!isComplete) i += 0;
}
}
이제 다중 스레드 환경의 JIT 컴파일러 / 최적화 프로그램이 TheHelper클래스를 처리 할 때 메서드 시작시 일부 레지스터 또는 스택 프레임 의 값 을 실제로 캐시 하고 메서드가 끝날 때까지 새로 고칠 수 없다고 상상할 수 있습니다 . 스레드 및 메서드가 "= true"가 실행되기 전에 끝나지 않을 것이라는 보장이 없기 때문입니다. 따라서 보장이 없다면 캐시하지 않고 힙 개체를 매번 읽는 대신 한 번 읽는 성능 향상을 얻으십시오. 되풀이.falseBody()
이것이 바로 키워드 volatile가 존재 하는 이유 입니다.
이 헬퍼 클래스가 조금 더 정확 해지 려면 1) 다중 스레드 환경에서 다음이 있어야합니다.
public volatile bool isComplete = false;
하지만 물론 자동 생성 된 코드이므로 추가 할 수 없습니다. 더 나은 접근 방식은 lock()읽기 및 쓰기에 대한 s 를 추가 isCompleted하거나 베어 메탈을 시도하는 대신 다른 즉시 사용 가능한 동기화 또는 스레딩 / 태스킹 유틸리티를 사용하는 것 입니다 . GC, JIT 및 (..))이있는 CLR의 C #이기 때문입니다.
디버그 모드의 차이는 아마도 디버그 모드에서 많은 최적화가 제외 되었기 때문에 발생하므로 화면에 표시된 코드를 디버그 할 수 있습니다. 따라서 while (!isComplete)중단 점을 설정할 수 있도록 최적화되지 않았으므로 isComplete메서드 시작시 레지스터 또는 스택에 적극적으로 캐시되지 않으며 모든 루프 반복에서 힙의 개체에서 읽습니다.
BTW. 그건 내 추측 일뿐입니다. 나는 그것을 컴파일하려고 시도조차하지 않았다.
BTW. 버그가 아닌 것 같습니다. 매우 모호한 부작용에 가깝습니다. 또한 내가 맞다면 언어 부족 일 수 있습니다. C #은 캡쳐되고 클로저의 멤버 필드로 승격되는 지역 변수에 '휘발성'키워드를 배치 할 수 있어야합니다.
1) 아래에서 Eric Lippert의 의견 volatile및 / 또는 코드에 의존하는 코드 volatile가 안전한지 확인하는 데 관련된 복잡성 수준을 보여주는 매우 흥미로운 기사를 참조하십시오. ..uh, good ..uh, OK라고합시다.