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


답변:


141

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

데이터 경쟁의 정의는 매우 명확하므로 검색을 자동화 할 수 있습니다. 데이터 경합은 서로 다른 스레드의 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에 쓰기가 이어질 때 기술적으로는 데이터 경쟁이 없지만 경쟁 조건이 있다고 말합니다.

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

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


1
"데이터 경쟁 (...) 이러한 액세스 사이에서 특정 순서를 요구하는 동기화가 없습니다." 나는 약간 혼란 스럽습니다. 귀하의 예에서 작업은 두 순서 (= 1 다음 = 2 또는 그 반대)로 발생할 수 있습니다. 데이터 경쟁이 아닌 이유는 무엇입니까?
josinalvo

6
@josinalvo : 데이터 경쟁에 대한 기술적 정의의 아티팩트입니다. 요점은 두 액세스 사이에 잠금 해제와 잠금 획득 (가능한 주문 중 하나에 대해)이 있다는 것입니다. 정의에 따라 잠금 해제 및 잠금 획득은 두 액세스 간의 순서를 설정하므로 데이터 경쟁이 없습니다.
Baris Kasikci

동기화 는 작업간에 특정 순서를 요구하지 않으므로이를 표현하는 데 그리 운 좋은 방법이 아닙니다. 반면에 JMM은 각 읽기 작업에 대해 데이터 경쟁에서도 관찰되는 명확한 쓰기 작업이 있어야한다고 지정합니다. 전 발생 및 동기화 순서를 명시 적으로 언급하는 것을 피하는 것은 어렵지만 JLS 정의조차도 전 발생 을 언급하는 데 잘못된 것입니다 . 정의에 따라 두 개의 동시 휘발성 쓰기가 데이터 경쟁을 구성합니다.
Marko Topolnik

@BarisKasikci "주문을 설정합니다"는 내가 생각하는 한 실제 의미가 없습니다. 그들은 단지 족제비 말입니다. 저는 솔직히 "데이터 경쟁"이 원격으로 유용한 개념이라고 믿지 않습니다. 말 그대로 여러 스레드에서 액세스하는 모든 메모리 위치가 "데이터 경쟁"으로 간주 될 수 있기 때문입니다
Noldorin

릴리스-획득 쌍은 항상 주문을 설정합니다. 일반적인 설명은 길지만 간단한 예는 신호 대기 쌍입니다. @Noldorin "정확한 순서"는 동시성 이론 (관계 이전에 발생한 관계에 대한 Lamport의 주요 논문 참조) 및 분산 시스템의 핵심 개념 인 순서 전 발생을 나타냅니다. 데이터 레이스는 많은 문제를 야기한다는 점에서 유용한 개념입니다 (예 : C ++ 메모리 모델에 따른 정의되지 않은 의미 체계, Java의 매우 복잡한 의미 체계 등). 그들의 탐지와 제거는 연구와 실습에서 방대한 문헌을 구성합니다.
바리스 Kasikci

20

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

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

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

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

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


1
data raceJLS에서 실제로 의미 하는 바를 설명하는 답변에 줄 을 서실 수 있습니까?
Geek

@geek "JLS"라는 단어는 JLS의 관련 섹션에 대한 하이퍼 링크입니다.
Marko Topolnik 2013 년

@MarkoTopolnik 나는 예제에서 약간 혼란 스럽습니다. "나는 변덕 스럽기 때문에 데이터 경쟁이 없습니다"라고 설명해 주시겠습니까? Voltility는 그것이 보이도록 보장했지만 여전히 : 1) 동기화되지 않고 여러 스레드가 동시에 읽고 쓸 수 있으며 2) 공유되지 않은 최종 필드이므로 실제로 Java Concurrency in Practice에 따르면 (아래 인용) , 데이터 경쟁이지 경쟁 조건이 아닙니다.
aniliitb10 apr

@ aniliitb10 문맥에서 벗어난 간접 따옴표에 의존하는 대신 내 답변에서 링크 한 JLS 섹션 17.4를 검토해야합니다. 휘발성 변수에 대한 액세스 는 §17.4.2에 정의 된 동기화 작업 입니다.
Marko Topolnik

@ aniliitb10 Votaltiles는 액세스를 주문할 수 있기 때문에 데이터 경쟁을 유발하지 않습니다. 즉, 이러한 방식 또는 그런 방식으로 순서를 추론하여 다른 결과를 얻을 수 있습니다. 데이터 경쟁에서는 주문을 추론 할 방법이 없습니다. 예를 들어, 각 스레드의 i ++ 작업은 해당 로컬 캐시 값 i에서 발생할 수 있습니다. 전 세계적으로 특정 언어 메모리 모델을 사용하지 않는 한 프로그래머의 관점에서 이러한 작업을 주문할 방법이 없습니다.
Xiao-Feng Li

3

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

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

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


질문에는 언어에 구애받지 않는 태그가 있습니다.
martinkunev

1

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

1. 의미론

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

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

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

2. 왜 다른가?

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

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

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

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

3. 언어 메모리 모델

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

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

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

4. 결론

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.