묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
Lock-Free Stack #2 01:16쯤에 말씀하신 동시에 TryPop() 할 경우..질문입니다.
안녕하세요!동시에 TryPop() 을 할 경우, oldHead를 참조하는 스레드가 있을 수 있고 이 때 무작정 해제해버리면 use after delete 오류가 발생할 수 있다고 하셨습니다.TryDelete() 함수를 구현하실 때, 1 == popCount 안에서 delete old_head 를 할 때는 이미 분리했으니까 괜찮다고 하셨는데요. 첫 문단에서 언급한 부분과 반대되는 말을 하신 것 같다는 느낌을 받았습니다.제가 생각했을 때는, 저희 수업 때 사용하신 예제 코드에서는, cas 연산을 통해 oldHead 를 분리했기 때문에 use after delete 오류가 안 날 것 같단 생각이 들더라고요.궁금해서 테스트를 해봤습니다.TryDelete() 함수를 호출하지 않고 바로 delete old_head; 를 호출하는 코드를 넣었고요, 지금 이 질문을 올리고 있는 와중에 push() 는 sleep_for(1ms)를 주고 pop()을 하는 스레드는 5개를 돌려서, 진단 도구에서 확인되는 프로세스 메모리가 1mb이 유지되게 설정해서 30분 넘게 돌려보고 있습니다. 오류가 안 나고 있는 광경을 목격 중입니다. 그냥 운이 좋은 걸까요?오류가 발생하지 않는 이유는 다음과 같다 생각합니다.atomic 변수인 head에서 cas 연산을 하기 때문에 간발의 차이로 먼저 다음 코드를 통과한 스레드가 획득한 oldHead는 다른 스레드가 이후에 될 oldHead와 같지 않다. while 조건에서 true를 반환하는 순간 head는 head의 next를 가리키고 있게 되기 때문이다. compare_exchange_weak() 함수가 true를 반환하면 head는 원자적으로 head->next를 가리키는 상태가 된다.while (old && false == _head.compare_exchange_weak(old, old->next)) return false; 고견 부탁드립니다.멋진 강의 감사합니다.
-
미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
lock lock-free 성능 차이가 없다는 부분에 대하여
강의 하실때 lock방식과 lock-free방식에 성능 차이가 없다는 것처럼 이야기 하셔서, 왜 성능 차이가 없을까 라는 것을 생각 해 보면, 주로 성능 차이가 어디에서 오냐를 먼저 고민해 봐야 할 것입니다. 저의 생각으로는, 정상적인 상황에서 Context switching으로 인해 커널 - 유저 모드 전환에서 큰 비용이 발생한다고 생각이 됩니다. 전통적인 Event 방식이 대표적인 예라고 생각합니다. 작업하신 lock-free 방식을 볼때마다 spinlock이 계속 떠올랐습니다. 결국 User mode에서 polling하고 있는 것이 겠지요. 어디서 주워 듣기로는 요즘 lock들도 커널 모드로 바로 들어가 일정시간 잠들지 않고, user mode에서 3000번, 5000번 정도 while을 돌면서 소유권을 얻을 수 있는지 확인한다고 합니다. 그 횟수동안 얻지 못하면 sleep 되는 식으로 알고 있습니다. 결국 빠르게 소유권이 전환되는 상황이라면 user mode에서만 작업이 발생하기 때문에 큰 overhead가 없다고 볼 수 있을 것 같습니다. 이러한 이유로 두 방식의 성능 차이가 거의 없다고 보는데 다른 분들의 의견이 궁금합니다.
-
미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
Lock Free 관련하여 질문드립니다.
예제 코드를 보면 TryPop을 아래와 같이 구현하고 있습니다. bool TryPop(T& outData) { Node* oldHead = _head.load(); Node* expected = oldHead; Node* desired = oldHead->Next; while (oldHead && false == _head.compare_exchange_weak(expected, desired)) { // Null } if (_head.load() == nullptr) { return false; } // 이하 생략 } 혹시 위의 코드에서 아래처럼 미리 Null check를 해도 무관할까요? bool TryPop(T& outData) { if (_head.load() == nullptr) { return false; } Node* oldHead = _head.load(); Node* expected = oldHead; Node* desired = oldHead->Next; while (false == _head.compare_exchange_weak(expected, desired)) { // Null } // 이하 생략 } 실제로 돌렸을 때는 문제가 없었는데, 혹시 제가 간과한 부분이 있나 하여 여쭤봅니다.