[Server Programming/cpp] DeadLock

예제 상황

User를 관리하는 UserManager 클래스, Account를 관리하는 AccountManager 클래스가 있을때,

각각의 Manager 클래스에서 서로 다른 클래스 자원을 가져올때 뮤텍스 락을 사용하고 이때, 데드락이 발생하는 상황

데드락 상황 예제

메인스레드

...
void Func1() {
	for (int32 i = 0; i < 1; i++)
	{
		UserManager::instance()->ProcessSave();
	}
}

void Func2() {
	for (int32 i = 0; i < 1; i++)
	{
		AccountManager::instance()->ProcessLogin();
	}
}

int main()
{
	std::thread t1(Func1);
	std::thread t2(Func2);
    ...

ProcessSave

void UserManager::ProcessSave() {
	//userLock
	lock_guard<mutex> guard(_mutex);

	//accountLock
	AccountManager::instance()->GetAccount(100);

	//TODO
}

t1 쓰레드가 먼저 진행되었다는 가정으로

  1. userLock GetAccount를 통해 Account Lock을 시도한다.

GetAccount

	Account* GetAccount(int32 id) {
		// Account Lock
		lock_guard<mutex> guard(_mutex);
		return nullptr;
	}

하지만 이때 Account Lock을 얻지 못해 대기상황이 발생하였다. 왜일까?

돌아와서 t1 쓰레드만 동시간대에 돌아가는 것이 아니며, 동시간대에 t2 쓰레드가 AccountLock을 소유했기 때문에 그런 상황이 발생한것이다.

t2의 ProcessLogin

void AccountManager::ProcessLogin() {
	//accountLock
	lock_guard<mutex> guard(_mutex);

	//userLock
	User* user = UserManager::instance()->GetUser(100);
}

이 곳에서 먼저 account Lock을 소유하고 GetUser를 호출해 user Lock을 하려고 할때 t1이 이미 user Lock을 소유한 상황으로 뮤텍스를 얻지 못해 대기에 걸린다.

t2의 getuser

User* GetUser(int32 id) {
	// User Lock
	lock_guard<mutex> guard(_mutex);
	return nullptr;
}

두 쓰레드가 서로를 기다리니 영원히 끝나지 않아 이런 상황을 데드락이라고 부른다!

해결 방법

  • 순서를 맞추기 t1 쓰레드에서는 user, account 순으로 lock을 t2 쓰레드에서는 account, user 순으로 lock을 소유하려고 하니 서로 락을 하는 순서가 달라서 문제가 발생한다.

진행 중에 이 순서를 항상 일정하게 보장하는 방법을 쓴다면 데드락을 방지할 수 있을것 이다.

위에 나온 코드에서는 t1의 proccessSave()에서 user, account 를 account -> user로 순서를 바꿔주면 해당 프로그램의 데드락은 해결이 가능하다.


`ProcessSave`
```cpp
void UserManager::ProcessSave() {
	//accountLock을 먼저!
	AccountManager::instance()->GetAccount(100);

    //userLock
	lock_guard<mutex> guard(_mutex);
	//TODO
}

출처

인프런 Rookiss, "[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버"