만약 하나의 공유 자원에 여러 개의 쓰레드가 접근을 했을 경우
그 자원의 값이 뒤죽 박죽이 될 수 있으니 한 쓰레드가 그 자원을 독점 할 수 있도록 도와 준다.
Lock 구현 방법으로는 3가지가 있다.
1.Spin lock
- 이 방법은 해당 자원의 임계영역이 풀릴 떄 까지 무작정 기다리는 것을 말한다.
- 무식한 방법이지만 임계영역이 금방 풀릴 것이 기대가 있다면 사용하기 좋다.
하지만 임계영역이 풀리지 않는 다면 매우 비효율적이다.
- 하지만 cpu 점유가 높아 지고 다른 쓰레드를 방해할 수 있다.
아래는 Spin lock의 예시이다.
#include <iostream>
#include <thread>
#include <atomic>
std::atomic_flag lock = ATOMIC_FLAG_INIT; // std::atomic_flag 객체 생성
void print(int num)
{
while (lock.test_and_set(std::memory_order_acquire)) // 뮤텍스 잠금
{
// 뮤텍스가 해제될 때까지 busy-waiting
}
std::cout << "Thread " << num << " started" << std::endl;
lock.clear(std::memory_order_release); // 뮤텍스 해제
}
int main()
{
std::thread t1(print, 1);
std::thread t2(print, 2);
t1.join();
t2.join();
return 0;
}
2. Sleep
- 랜덤적으로 대기하는 것을 말한다.
- 효율적으로 보이지만 운이 좋지 않으면 계속 대기 할 수도 있따.
- context switching이 발생해 그 만큼 이동거리가 발생한다.
#include <iostream>
#include <mutex>
#include <thread>
#include <chrono>
std::mutex mtx;
void print(int num) {
bool lockAcquired = false;
do {
mtx.lock();
if (lockAcquired) {
std::cout << "Thread " << num << " critical section end" << std::endl;
mtx.unlock();
break;
}
std::cout << "Thread " << num << " critical section start" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1)); // 1초 동안 sleep
lockAcquired = true;
} while (!lockAcquired);
}
int main() {
std::thread t1(print, 1);
std::thread t2(print, 2);
t1.join();
t2.join();
return 0;
}
3. Event
- 해당 쓰레드에게는 효과적인 방법 일 수 있지만 다른 리소스를 사용한다.
- 이 방법도 context switching이 발생한다.
이벤트는 추후에 구현하도록 하겠다.
반응형