[C++] Mutex

MEROBOT
|2024. 1. 21. 13:57

MUTEX

GVINS 코드를 분석하면서 Mutex를 처음 접했다.

Visual, inertial, gnss, pose... 수많은 함수가 각각의 데이터에 접근해야 하기 때문에 발생하는 문제를 방지해 준다.

여러 쓰레드가 데이터에 접근을 하다 보면 일명 꼬이는 상황이 발생한다.

이렇게 되면 시스템이 불안정 해지기 때문에 Mutex를 이용해서 해당 영역에 대한 접근을 제한한다.

 

유튜브 강의를 시청 후 간단히 정리하였다.

강의 정보는 글 하단에 기재.

일반적인 mutex 사용

#include <mutex>

std::mutex mutex1


mutex1.lock();
//
//
// Critical section(mutex로 보호하고 싶은 라인)
//
//
mutex1.unlock();

 

일반적인 mutex 단점 -> 하나의 쓰레드가 볼일 마치고 대기중인 쓰레드가 순서대로 들어오는게 아니라 무작위로 들어옴

 

mutex.try_lock

 

#include <mutex>

std::mutex mutex1


mutex1.try_lock();
//
//
// Critical section(mutex로 보호하고 싶은 라인)
//
//
mutex.unlock();

try_lock()은 mutex 내부 critical section에 진입하면 True, 진입 못하면 False 돌려줌.

이 것을 이용하면 진입 못했을 때 어떻게 할 것인지도 지정 가능.

 

하지만 일반적으로 lock, unlock을 사용하다가 깜빡하고 unlock을 안해주는 상황, 내부에서 예외처리할 때 잘 지정해주지 않으면 버그 발생.

주의해야함.

이럴 때 아래 lock_gaurd사용.

mutex.lock_guard

위에 빨간 글씨같은 상황을 막기 위해 좋은 기능이다.

스코프 내에서 한 번 선언 해주면 따로 unlock을 명시 해주지 않아도 알아서 unlock을 해줌.

#include <mutex>
#include <thread>
#include <mutex>

struct MInt
{
	std::mutex mtx;
    int num = 0;
};

void plus1(MInt & mi)
	//스코프의 시작에 선언해주면 끝날때 자동으로 unlock
{	//여기가 스코프 시작
	const std::lock_guard<std::mutex> lock(mi.mtx);
    mi.num++;
}	//스코프 끝

int main()
{
	MInt mi;
    std::thread t1(plus1,std::ref(mi));
    std::thread t2(plus1,std::ref(mi));
	
    t1.join();
    t2.join();
    
    std::cout << "num: " << mi.num << std::endl;
}

만약 함수가 끝나기 전에 unlock을 해주고 싶으면?

스코프 하나 더 만들어주면 된다.

void plus1(MInt & mi)

{	
	{ //스코프 하나 더 만들어주기
		const std::lock_guard<std::mutex> lock(mi.mtx);
    }
    mi.num++;
}

 

mutex.unique_lock

lock_guard는 제한적인 용도로 사용되는 반면 unique_lock은 여러 기능이 있음.

lock_guard는 copy도 안됨.

unique_lock의 많은 기능중에 중요한 기능은 move가 가능.

mutex_lock을 리소스로 관리할 수 있게 만들어줌.

 

lock_guard VS unique_lock 둘 중 어떤 것을 써야하냐?

가능하면 더 가벼고 간단한 프로그래밍이 가능한 lock_guard를 사용.

하지만 더 세부적인 기능을 사용하고 싶을 때 unique_lock을 사용.

 

강의 출처

https://www.youtube.com/watch?v=0pvx4k0lsoA&list=PLDV-cCQnUlIYBAg9tm1pNUyiYkSvwj1YW&index=1