MMOServer

SpinLock(C++)

이야기prog 2025. 3. 13. 15:54
#include "pch.h"
#include <thread>
#include <atomic>
#include <mutex>

class SpinLock {

public:
	void lock() {
		bool expected = false;
		bool desired = true;
		/*if (locked_ == expected) { // CAS
			expected = locked_;
			locked_ = desired;
			return true;
		}
		else {
			expected = locked_;
			return false;
		}*/
		while (locked_.compare_exchange_strong(expected, desired) == false) { // CAS atomic하게 하드웨어에서 실행함.
			expected = false;
		}
	}

	void unlock() {
		locked_.store(false);
	}
private:
	atomic<bool> locked_ = false;
};

int32 sum;
SpinLock m;
void Sub() {
	for (int32 i = 0; i < 10'0000; ++i) {
		std::lock_guard<SpinLock> lockGuard(m);
		sum--;
	}
}

void Add() {
	for (int32 i = 0; i < 10'0000; ++i) {
		std::lock_guard<SpinLock> lockGuard(m);
		sum++;
	}
}

int main() {
	std::thread t1(Sub);
	std::thread t2(Add);

	t1.join();
	t2.join();
	std::cout << sum;
	return 0;
}

SpinLock은 어떤 한 쓰레드가 소유한 lock을 다른 쓰레드가 소유하고 싶을 때, lock을 소유한 쓰레드가 unlock하기 전까지 계속해서 대기하면서 unlock하길 기다리는 방법이다.

즉 while루프를 계속 돌기 때문에, unlock할 때까지의 시간이 길어진다면 그 만큼 cpu점유율이 높아지고 자원이 낭비가 된다.

허나, context switching의 오버헤드가 없기때문에 금방 unlock한다는 확신이 있다면 SpinLock이 효율적일 수 있다.

atomic클래스의 compare_exchange_strong으로 구현 가능한대, 저 함수자체가 atomic하게 하드웨어의 도움을 받아서 실행되기 때문에 내부의 비교하고 값을 교환하는 연산이 atomic하게 즉 한번에 이루어지게 된다.

 

 위에 주석된 코드가 compare_exchange_strong의 의사코드이다.

 

std::this_thread::sleep_for(std::chrono::microseconds(100));처럼 추가로 지금 실행중인 쓰레드를 강제로 정지상태로 만들어서 100ms마다 스케줄링을 다시 받아서 실행하게 할 수도 있는데, thread context switching 비용이 크기 때문에, 적절히 섞어 쓰는게 좋다. 굳이 100ms말고 원하는 초를 적어넣으면 됨.

'MMOServer' 카테고리의 다른 글

Future(C++)  (0) 2025.03.13
Event, Condition_Variable(C++)  (0) 2025.03.13
Mutex(C++)  (0) 2025.03.11
Atomic(C++)  (0) 2025.03.11
Thread(C++)  (0) 2025.03.11