[multithreading] 동시 프로그래밍의 맥락에서 “데이터 경합”과 “경쟁 조건”은 실제로 동일한 것입니다.

나는 종종 이러한 용어가 동시 프로그래밍의 맥락에서 사용되는 것을 발견합니다. 동일하거나 다른가요?



답변

아니요, 그들은 같은 것이 아닙니다. 그들은 서로의 하위 집합이 아닙니다. 그것들은 또한 서로에게 필요하거나 충분한 조건이 아닙니다.

데이터 경쟁의 정의는 매우 명확하므로 검색을 자동화 할 수 있습니다. 데이터 경합은 서로 다른 스레드의 2 개의 명령어가 동일한 메모리 위치에 액세스 할 때 발생하며, 이러한 액세스 중 적어도 하나는 쓰기이며 이러한 액세스 사이에 특정 순서를 요구 하는 동기화가 없습니다 .

경합 조건은 의미 오류입니다. 잘못된 프로그램 동작을 유발하는 이벤트의 타이밍 또는 순서에서 발생하는 결함입니다. 많은 경쟁 조건은 데이터 경쟁으로 인해 발생할 수 있지만 반드시 필요한 것은 아닙니다.

x가 공유 변수 인 다음의 간단한 예를 고려하십시오.

Thread 1    Thread 2

 lock(l)     lock(l)
 x=1         x=2
 unlock(l)   unlock(l)

이 예에서 스레드 1과 2의 x에 대한 쓰기는 잠금으로 보호되므로 런타임에 잠금을 획득하는 순서에 따라 실행되는 순서대로 항상 발생합니다. 즉, 쓰기의 원자 성은 깨질 수 없습니다. 모든 실행에서 두 쓰기 사이에 관계가 있기 전에 항상 발생합니다. 우리는 어떤 쓰기가 다른 것보다 먼저 일어나는지 알 수 없습니다.

잠금이이를 제공 할 수 없기 때문에 쓰기 사이에 고정 된 순서가 없습니다. 프로그램의 정확성이 손상되면, 예를 들어 스레드 2에 의한 x 쓰기 다음에 스레드 1의 x에 쓰기가 이어질 때 기술적으로는 데이터 경쟁이 없지만 경쟁 조건이 있다고 말합니다.

데이터 경쟁보다 경쟁 조건을 감지하는 것이 훨씬 더 유용합니다. 그러나 이것은 또한 달성하기 매우 어렵습니다.

반대 예제를 구성하는 것도 간단합니다. 블로그 게시물은 간단한 은행 거래 예를 통해 차이점을 잘 설명합니다.


답변

Wikipedia에 따르면 “경주 상태”라는 용어는 최초의 전자 논리 게이트 시대부터 사용되어 왔습니다. Java의 맥락에서 경쟁 조건은 파일, 네트워크 연결, 스레드 풀의 스레드 등과 같은 모든 리소스와 관련 될 수 있습니다.

“데이터 경쟁”이라는 용어는 JLS에서 정의한 특정 의미에 가장 적합합니다 .

가장 흥미로운 경우는 다음과 같은 간단한 예에서와 같이 데이터 경쟁과 매우 유사하지만 여전히 그렇지 않은 경쟁 조건입니다.

class Race {
  static volatile int i;
  static int uniqueInt() { return i++; }
}

i변동성이 있기 때문에 데이터 경쟁이 없습니다. 그러나 프로그램 정확성의 관점에서 read i, write 라는 두 작업의 비원 자성으로 인해 경쟁 조건이 i+1있습니다. 여러 스레드가에서 동일한 값을받을 수 있습니다 uniqueInt.


답변

아니요, 그들은 다르며 둘 다 하나 의 하위 집합 이 아니며 그 반대도 마찬가지입니다.

경합 조건이라는 용어는 종종 관련 용어 데이터 경합과 혼동되는데, 이는 공유 된 비 최종 필드에 대한 모든 액세스를 조정하는 데 동기화가 사용되지 않을 때 발생합니다. 스레드가 다음에 다른 스레드에서 읽을 수있는 변수를 쓰거나 두 스레드가 동기화를 사용하지 않는 경우 다른 스레드에서 마지막으로 쓴 변수를 읽을 때마다 데이터 경합이 발생할 위험이 있습니다. 데이터 경합이있는 코드에는 Java 메모리 모델에 정의 된 유용한 의미가 없습니다. 모든 경쟁 조건이 데이터 경쟁 인 것은 아니며 모든 데이터 경쟁이 경쟁 조건 인 것은 아니지만 둘 다 동시 프로그램이 예측할 수없는 방식으로 실패하게 만들 수 있습니다.

뛰어난 책 -Joshua Bloch & Co의 Java Concurrency in Practice에서 발췌.


답변

요약 : 데이터 경합과 경합 조건의 차이는 문제 공식화의 특성과 정의되지 않은 동작과 잘 정의되었지만 결정되지 않은 동작 사이의 경계를 그릴 위치에 따라 다릅니다. 현재의 차이점은 일반적이며 프로세서 설계자와 프로그래밍 언어 간의 인터페이스를 가장 잘 반영합니다.

1. 의미론

데이터 경쟁은 특히 동일한 메모리 위치에 대해 동기화되지 않은 충돌 “메모리 액세스”(또는 작업 또는 작업)를 나타냅니다. 메모리 액세스에 충돌이 없는데도 작업 순서에 의해 발생하는 불확실한 동작이 여전히 존재하는 경우 경쟁 조건입니다.

여기서 “메모리 액세스”에는 특정 의미가 있습니다. 추가 의미가 적용되지 않은 “순수한”메모리로드 또는 저장 작업을 참조합니다. 예를 들어, 한 스레드의 메모리 저장소는 데이터가 메모리에 기록되는 데 걸리는 시간을 (필연적으로) 알지 못하고 마지막으로 다른 스레드로 전파됩니다. 또 다른 예를 들어, 동일한 스레드에 의해 다른 위치에 다른 위치로 저장되기 전에 한 위치에 메모리 저장이 있다고해서 메모리에 기록 된 첫 번째 데이터가 두 번째 데이터보다 앞서 있음을 보장하지 않습니다. 결과적으로 이러한 순수 메모리 액세스의 순서는 (필연적으로) “합리적” 일 수 없으며, 달리 잘 정의되지 않는 한 모든 일이 발생할 수 있습니다.

“메모리 액세스”가 동기화를 통한 순서 지정 측면에서 잘 정의 된 경우 추가 시맨틱은 메모리 액세스의 타이밍이 불확실 하더라도 동기화를 통해 순서가 “합리적” 일 수 있도록 보장 할 수 있습니다 . 메모리 액세스 사이의 순서는 추론 할 수 있지만 반드시 결정된 것은 아니므로 경쟁 조건입니다.

2. 왜 다른가?

그러나 경쟁 조건에서 순서가 여전히 불확실한 경우 데이터 경쟁과 구별해야하는 이유는 무엇입니까? 그 이유는 이론적이기보다는 실용적입니다. 프로그래밍 언어와 프로세서 아키텍처 간의 인터페이스에 차이가 있기 때문입니다.

현대 아키텍처에서 메모리로드 / 저장 명령은 일반적으로 비 순차 파이프 라인, 추측, 다중 레벨 캐시, CPU- 램 상호 연결, 특히 다중 코어 등의 특성으로 인해 “순수한”메모리 액세스로 구현됩니다. . 불확실한 타이밍과 순서로 이어지는 많은 요인이 있습니다. 모든 메모리 명령에 대해 순서를 적용하려면 특히 멀티 코어를 지원하는 프로세서 설계에서 막대한 불이익이 발생합니다. 따라서 주문 의미 체계에는 다양한 장벽 (또는 울타리)과 같은 추가 지침이 제공됩니다.

데이터 경쟁은 충돌하는 메모리 액세스 순서를 추론하는 데 도움이되는 추가 펜스없이 프로세서 명령을 실행하는 상황입니다. 결과는 불확실 할뿐만 아니라 매우 이상 할 수도 있습니다. 예를 들어, 서로 다른 스레드에 의해 동일한 단어 위치에 두 번 기록하면 각 단어의 절반을 기록하거나 로컬 캐시 된 값에 대해서만 작동 할 수 있습니다. -프로그래머의 관점에서 볼 때 정의되지 않은 동작입니다. 그러나 그들은 (보통) 프로세서 설계자의 관점에서 잘 정의되어 있습니다.

프로그래머는 코드 실행 을 추론 할 방법이 있어야합니다 . 데이터 경쟁은 그들이 이해할 수없는 것이므로 항상 피해야합니다 (일반적으로). 그렇기 때문에 낮은 수준의 언어 사양은 일반적으로 경쟁 조건의 잘 정의 된 메모리 동작과 다른 정의되지 않은 동작으로 데이터 경쟁을 정의합니다.

3. 언어 메모리 모델

프로세서마다 다른 메모리 액세스 동작, 즉 프로세서 메모리 모델이있을 수 있습니다. 프로그래머가 모든 최신 프로세서의 메모리 모델을 연구하고 그로부터 혜택을받을 수있는 프로그램을 개발하는 것은 어색합니다. 언어가 메모리 모델을 정의하여 해당 언어의 프로그램이 항상 메모리 모델이 정의한대로 예상대로 작동하도록하는 것이 바람직합니다. 이것이 Java와 C ++에 메모리 모델이 정의 된 이유입니다. 언어 메모리 모델이 서로 다른 프로세서 아키텍처에 적용되도록하는 것은 컴파일러 / 런타임 개발자의 부담입니다.

즉, 언어가 프로세서의 저수준 동작을 노출하지 않으려는 경우 (현대 아키텍처의 특정 성능 이점을 기꺼이 희생하려는 경우) “순수한”세부 정보를 완전히 숨기는 메모리 모델을 정의 할 수 있습니다. 메모리 액세스가 가능하지만 모든 메모리 작업에 대해 순서 지정 의미를 적용합니다. 그런 다음 컴파일러 / 런타임 개발자는 모든 프로세서 아키텍처에서 모든 메모리 변수를 휘발성으로 처리하도록 선택할 수 있습니다. 스레드간에 공유 메모리를 지원하는 이러한 언어의 경우 데이터 경합이 없지만 완전한 순차 일관성의 언어를 사용하더라도 여전히 경합 상태 일 수 있습니다.

다른 한편으로, 프로세서 메모리 모델은 예를 들어 초기 프로세서가했던 것처럼 순차 일관성을 구현하는 것과 같이 더 엄격 할 수 있습니다 (또는 덜 완화되거나 더 높은 수준에서). 그런 다음 모든 메모리 작업이 정렬되고 프로세서에서 실행되는 언어에 대한 데이터 경쟁이 없습니다.

4. 결론

원래 질문으로 돌아가서 IMHO는 데이터 경쟁을 경쟁 조건의 특별한 경우로 정의하는 것이 좋으며 한 수준의 경쟁 조건이 더 높은 수준의 데이터 경쟁이 될 수 있습니다. 그것은 문제 공식화의 본질과 정의되지 않은 행동과 잘 정의되었지만 결정되지 않은 행동 사이의 경계를 그리는 위치에 달려 있습니다. 현재의 관례는 언어-프로세서 인터페이스에서 경계를 정의하고 항상 그렇다는 것을 의미하지는 않습니다. 그러나 현재의 관습은 아마도 프로세서 설계자와 프로그래밍 언어 사이의 최첨단 인터페이스 (및 지혜)를 가장 잘 반영합니다.


답변