1. CAS(Compare And Swap)

비교 후 교체, 경쟁 조건을 방지하기 위한 연산 방식.

 

(1) compare_exchange_strong ; atomic 클래스의 함수

커스텀 락을 만들 때 효과적인 함수.

bool expected = false;
bool desired = true;

if (_flag == expected)
{
	_flag = desired;
	return true;
}
else
{
	expected = _flag;
	return false;
}

expected는 예상되는 값(이전 값), desired는 변경되기를 원하는 값(결과 값)이다.

_flag를 체크해 경쟁 조건이 발생하지 않아 값이 이전과 변함이 없다면(expected) 결과 값으로 _flag의 값을 변경시키고 true를 반환한다.

아닐 경우에는 expected에 현재 값을 저장하고 false를 반환한다.

위의 함수를 사용하여 경쟁 조건을 방지할 수 있다.

 

(2) 문법

while (_flag.compare_exchange_strong(expected, desired) == false)
{
	expected = false;
}

이 코드를 사용하여 결과가 true가 될 때 까지 코드를 동작시킬 수 있다.

단 이 함수에는, 결과가 false일 때 expected의 값을 desired로 변환시키는 동작이 포함되어 있기 때문에, 항상 expected의 값을 원래의 값으로 초기화시켜야 한다는 점에 주의해야 한다.

 

 

(3) 활용

class Lock
{
public:
	void lock()
	{
		bool expected = false;
		bool desired = true;

		while (_flag.compare_exchange_strong(expected, desired) == false)
		{
			expected = false;
		}
	}

	void unlock()
	{
		_flag = false;
	}

private:
	atomic<bool> _flag = false;
};

Lock m;
vector<int> v;

void Push()
{
	for (int i = 0; i < 10000; i++)
	{
		std::lock_guard<Lock> lockGuard(m);

		v.push_back(i);
	}
}

int main()
{
	thread t1(Push);
	thread t2(Push);

	t1.join();
	t2.join();

	cout << v.size() << endl;
}

새로 만든 mutex를 본따 만든 Lock 클래스를 이용하여 lock_guard를 테스트하기 위하여 lock 함수와 unlock 함수를 오버로딩한 코드이다.

값이 정상적으로 2만이 뜨는 것을 볼 수 있다.

 

 

2. 스핀 락(Spin Lock)

뮤텍스와 같은 락은 다른 스레드가 작업을 종료할 때까지 대기열에서 대기하는 방법을 이용한다.

하지만 스핀 락을 사용하면 다른 스레드는 작업중인 스레드가 종료할 때(unlock)까지 계속 플래그를 검사하며 대기한다.

 

(1) 장점

- 간단한 경우에 짧은 순간에 락을 잡고 풀어준다는 확신이 있을 때 스핀 락을 사용하는 것이 성능에 있어 더 효율적이다.

(락을 획득하는 데 소요되는 시간이 짧다)

- 컨텍스트 스위칭을 최소화하여 CPU의 성능을 확보할 수 있다.

 

* 컨텍스트 스위칭(Context Switching)

CPU에서 실행되는 프로세스/스레드를 다른 프로세스/스레드로 교체하는 작업.

성능 비용이 많이 든다.

 

(2) 단점

- 계속 플래그를 체크하며 대기하기 때문에(Busy-Waiting) CPU의 자원을 많이 점유한다.

- 데드락이 발생할 수 있다.