#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 |