하아찡
[C++ 11] SleepLock 본문
이전에는 SpinLock을 살펴보았는데
이번에는 SleepLock을 살펴보겠습니다.
사실 SpinLock에 반복문 내부에 "일정시간 대기해라" 코드 한줄만 추가해주면 바로 SleepLock으로 변경하여 사용할 수 있습니다.
#include "pch.h"
#include <thread>
#include <atomic>
#include <mutex>
class SleepLock {
public:
void lock() {
bool expected = false;
bool desired = true;
while (!_locked.compare_exchange_weak(expected, desired)) {
expected = false;
//기존 SpinLock에서 SleepLock을 위해 추가된 코드
this_thread::sleep_for(100ms); //100ms동안 대기
}
}
void unlock() {
_locked.store(false);
}
private:
atomic<bool> _locked = false;
};
int32 sum = 0;
SleepLock sleepLock;
void Add() {
for (int i = 0; i < 100000; i++) {
lock_guard<SpinLock> guard(sleepLock);
sum += i;
}
}
void Sub() {
for (int i = 0; i < 100000; i++) {
lock_guard<SpinLock> guard(sleepLock);
sum -= i;
}
}
int main()
{
mutex m;
std::thread t1(Add);
std::thread t2(Sub);
if (t1.joinable()) {
t1.join();
}
if (t2.joinable()) {
t2.join();
}
cout << sum << endl;
}
기존에 SpinLock과 별 차이점이 없죠.
단지 Sleep이 추가돼서 해당 시간동안 CPU점유를 안하게됩니다.
긴 대기시간에 사용하면 좋겠죠?
물론 단점도 있습니다.
긴 대기시간동안 CPU를 점유하고 있으면 그것대로 단점이지만 SleepLock은 대기상태로 돌아가기 때문에 다른 쓰레드가 CPU를 점유할 수 있게 됩니다.
그 과정에서 문맥교환(Context Switching)이 발생합니다.
오히려 빠르게 점유했다가 풀어주는 상황에선 SpinLock을 사용하시는게 더 효과적입니다.
그러면 이러한 방식도 있지않을까요?
SpinLock을사용하다가 너무나도 오래동안 안나오면 SleepLock으로 변경해서 사용하는것도 가능하지 않을까요?
#include "pch.h"
#include <thread>
#include <atomic>
#include <mutex>
class AdaptiveLock {
public:
void lock() {
int32 spinCount = 0; // Spin 횟수
const int32 maxSpinCount = 1; // Spin 횟수 제한
bool expected = false;
bool desired = true;
while (!_locked.compare_exchange_weak(expected, desired)) {
expected = false;
if (++spinCount > maxSpinCount) {
cout << "Sleep으로 변경" << endl;
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // Sleep 전환
}
else {
std::this_thread::yield(); // CPU 양보
}
}
}
void unlock() {
_locked.store(false);
}
private:
atomic<bool> _locked = false;
};
int32 sum = 0;
AdaptiveLock adaptiveLock;
void Add() {
for (int i = 0; i < 100000; i++) {
lock_guard<AdaptiveLock> guard(adaptiveLock);
sum += i;
}
}
void Sub() {
for (int i = 0; i < 100000; i++) {
lock_guard<AdaptiveLock> guard(adaptiveLock);
sum -= i;
}
}
int main()
{
mutex m;
std::thread t1(Add);
std::thread t2(Sub);
std::thread t3(Add);
std::thread t4(Sub);
if (t1.joinable()) {
t1.join();
}
if (t2.joinable()) {
t2.join();
}
if (t3.joinable()) {
t3.join();
}
if (t4.joinable()) {
t4.join();
}
cout << sum << endl;
}
현재코드는 maxSpinCount 숫자를 낮췄습니다. 단순한 코드이다보닌깐 임의로 숫자를 낮추게됐습니다.
그래서 출력 결과를 살펴보면
결과값은 정상적으로 나오고 중간중간 Sleep으로 변경된 적이 있습니다.
위 코드처럼 일정 횟수가 넘어가면 SpinLock에서 SleepLock으로 변경도 가능하게 됩니다.
추가적인 예시
#include "pch.h"
#include <thread>
#include <atomic>
#include <mutex>
class AdaptiveLock {
public:
void lock() {
int32 spinCount = 0; // Spin 횟수
const int32 maxSpinCount = 100; // Spin 횟수 제한
bool expected = false;
bool desired = true;
while (!_locked.compare_exchange_weak(expected, desired)) {
expected = false;
if (++spinCount > maxSpinCount) {
//cout << "Sleep으로 변경" << endl;
//std::this_thread::yield();
std::this_thread::sleep_for(std::chrono::milliseconds(10000)); // Sleep 전환
}
else {
std::this_thread::yield(); // CPU 양보
}
}
}
void unlock() {
_locked.store(false);
}
private:
atomic<bool> _locked = false;
};
int32 sum = 0;
AdaptiveLock adaptiveLock;
queue<int32> q;
void Producer() {
while (true) {
{
unique_lock<AdaptiveLock> guard(adaptiveLock);
q.push(100);
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000000));
}
}
void Consumer() {
while (true) {
unique_lock<AdaptiveLock> guard(adaptiveLock);
if (q.empty() == false) {
//값이 있으면 빼온다
int32 data = q.front();
q.pop();
cout << data << endl;
}
}
}
int main()
{
mutex m;
std::thread t1(Producer);
std::thread t2(Consumer);
std::thread t3(Consumer);
std::thread t4(Consumer);
std::thread t5(Consumer);
if (t1.joinable()) {
t1.join();
}
if (t2.joinable()) {
t2.join();
}
if (t3.joinable()) {
t3.join();
}
if (t4.joinable()) {
t4.join();
}
if (t5.joinable()) {
t5.join();
}
cout << sum << endl;
}
queue에 값을 넣는 속도가 느린데 값을 빼올라고 하는 작업을 계속하는데 SpinLock으로 돌리다가 계속 답이없으면 SleepLock으로 변경했을때 CPU 점유율 차이입니다.
'C++ > 추가공부' 카테고리의 다른 글
[C++] 메모리 모델 (0) | 2024.12.27 |
---|---|
[C++ 11] condition variable(조건 변수) (0) | 2024.12.27 |
[C++] Event (0) | 2024.12.27 |
[C++ 11]std::atomic, SpinLock (1) | 2024.12.27 |
Volatile 예약어 (0) | 2024.10.30 |