멀티스레드 프로그래밍 - Data Race
멀티스레드 프로그래밍을 접하고 가장 처음 접하는 문제가 바로 Data Race 문제이다.
Data Race에 대해 설명할때 가장 자주 쓰이는 예시는 다음과 같다.
1. 전역변수 G가 있다. 이 값을 0으로 초기화한다.
2. 스레드 A가 G의 값을 1씩 n번 증가시킨다.
3. 스레드 B가 G의 값을 1씩 n번 감소시킨다.
당연하게도 A가 1씩 n번 더했고, B가 1씩 n번 뺐으니까 결과값은 0이 되어야한다.
허나, 이 일을 시키면 결과값은 0이되거나, 0이 아닐수도 있다.
왜 그런가 보면,
G++ 이라는 코드는 다시 3개의 어셈블리어 연산으로 해석된다.
1. 전역변수 G의 값을 레지스터로 가져온다.
2. 레지스터에 저장된 값을 1증가 시킨다.
3. 레지스터에 저장된 값 ( 즉, 1이 증가된 값)을 다시 전역변수 G에 저장한다.
메모리에 저장된 값에 직접 연산을 할 수 없기 때문에 이런일이 일어난다.
레지스터만이 산술 연산장치에 접근해 산술연산을 행할수 있는 것이다.
다시말해, 메모리에 저장된 값에 연산을 행하고 싶으면 레지스터에게 전달하고,
레지스터가 그 일을 해주는 것이다.
아마도 이런 연유에서 이름이 register가 된것이 아닌가 추측해본다.
다시 돌아가서,
G의 값이 0일때 부터 시작해보자. 문제가 되는 상황은 다음과 같은 상황이다.
스레드 A가 사용하는 레지스터를 RA라고 하고 스레드 B가 사용하는 레지스터를 RB라고 하자.
A스레드 - 메모리 -> 레지스터 G => RA = 0
A스레드 - 레지스터의 값 1증가 RA = 1
B스레드 - 메모리 -> 레지스터 G => RB = 0 (아직 RA값을 G에 안줬다)
A스레드 - 레지스터 -> 메모리 RA => G = 1
B스레드 - 레지스터의 값을 1 감소 RB = -1
B스레드 - 레지스터 -> 메모리 RB => G = -1
반드시 위와 같은 순서가 아니더라도 ,
원리가 아주 간단하다.
레지스터에서 연산이 끝난 결과가 메모리에 저장되기 전에,
다른 스레드가 메모리에서 값을 레지스터로 가져와서 거기다가 연산을 해버린다.
그리고 그 값을 저장한다.
값을 읽어오는 것은 큰문제가 되지 않는데, 다시 쓰는것에서 문제가 발생한다.
읽어온 데이터가 최신의 데이터인지 확신할 수 없기 때문이다.
이런 현상을 해결하기 위해, 언어표준이나 운영체제에서 다양한 방법들을 제공한다.
윈도우즈에서 제공하는 인터락함수나 크리티컬섹션이 그 예이다.
'멀티스레드 프로그래밍' 카테고리의 다른 글
멀티스레드 환경에서의 캐시 라인에 대한 주의사항 (0) | 2022.04.02 |
---|---|
Read/Write 스핀락 구현과 fairness에 대한 이야기 (0) | 2022.02.20 |
댓글
이 글 공유하기
다른 글
-
멀티스레드 환경에서의 캐시 라인에 대한 주의사항
멀티스레드 환경에서의 캐시 라인에 대한 주의사항
2022.04.02 -
Read/Write 스핀락 구현과 fairness에 대한 이야기
Read/Write 스핀락 구현과 fairness에 대한 이야기
2022.02.20