본문 바로가기

# 미사용

Test And Set(TAS) 명령어

Test And Set

Test And Set 명령어는 락을 사용하지 않는 기본적인 원자함수이며,
여느 원자함수가 그렇듯이 CPU 레벨에서 지원한다.
검사와 지정을 원자성을 가지고 수행한다.

bool test_and_set(bool& flag);

동작 내용

  • 주어진 불리언 값의 현재 값을 반환하고,
  • 그 불리언 값을 true 로 덮어쓴다.

반환 정보

  • true : 플래그가 원래 true 일 때.
  • false : 플래그가 원래 false 일 때. ( flag = true 명령도 동시에 일어남)

구현

기본 구현

기본적인 구현은 다음과 같으며,

매커니즘을 설명하기 위한 것 뿐이지 실제로 적용가능한 함수는 아니다.

중간에 문맥교환이 발생할 수 있기 때문이다.



아래는 간략한 상태도이다.

한번 세트된 플래그는 계속해서 true 만을 반환하지만,

재사용하고 싶다면 아래처럼 명시적으로 false 로 덮어쓰면 된다.


C++11 구현

test_and_set 명령어는 C++11에서 추가되었으며,

원자계 불리언 타입의 특화형인 atomic_flag 의 멤버 메소드로 구현되었다.



아래는 싱글 쓰레드에서의 예제이다.



조금 설명을 덧붙여보자면,

flag 의 현재상태가 false 인 상황에서만 if문이 실행되므로,

  • i == 0
  • i == 6 # i == 5 에서 clear 가 발생.

위의 두 차례에서 Hello! 가 출력된다.


응용

스핀락

test_and_set 의 성질을 활용하여 SpinLock을 구현할 수 있다.

다음 코드를 살펴보자.



제일먼저 test_and_set을 실행한 쓰레드만 반복문을 빠져나올 수 있는데,

이외의 다른 쓰레드는 clear 가 발생될 때 까지 반복문에 갇혀서 대기하게 된다.


즉, while을 빠져나온 쓰레드는 프로세스 내에서 1개만 존재하며,

상호배제 원리에 의하여 while 이하에 임계영역이 형성된다.


아래의 실행결과에서 동시에 while 문장을 빠져나온 쓰레드 쌍이 존재하지 않는 것에 주목하자.


실행결과

매 실행마다 달라질 수 있다.

0 enter ciritical section.

0 release lock.

1 enter ciritical section.

1 release lock.

4 enter ciritical section.

4 release lock.

2 enter ciritical section.

2 release lock.

3 enter ciritical section.

3 release lock.

done

카운터

스핀락을 구현했다면 원자성을 가지는 카운터를 만드는 것도 쉽다.

먼저 쓰레드에 안전하지 않은 버전부터 살펴보자.


10000 개의 쓰레드가 하나의 카운터에 대하여 개별적으로 10000 번씩 증가시킬 것 이다.

그러므로 카운터의 최종값은 10000 * 10000 이 되어야 한다.



원하지 않은 값이 나온 이유는 명백하고 단순하다.

같은 값에 대해 v+=1 을 수행한 쓰레드 쌍이 존재하기 때문이다.


예를 들어 v = 100 인 상태에서,

이미 어떤 쓰레드가 v = 100 + 1 으로 갱신중하려 함에도 불구하고,

v = 100 + 1 로 갱신하려는 다른 쓰레드가 있었기에 발생한 것 이다.


Test And Set 으로 문제를 해결할 수 있는 키 포인트는 간단하다.

값을 증가시키기 전에 한 쓰레드만 값 증가시키도록 순서를 직렬화 하는 것이다.


다음은 위 포인트를 적용하여 만든 쓰레드 세이프한 버전이다.