std::shared_ptr은 reference counter와 resource로 구성되어 있습니다.
reference counter 자체는 스레드로부터 '안전' 합니다.
그러나 resource의 접근은 안전하지 않습니다.
즉, reference counter 수정은 원자적(atomic)작업이며, resource가 정확히 한 번 삭제된다는 보장이 있습니다.
#include "shared_ptr.h"
#include "shared_ptr.hpp"
using namespace std;
shared_ptr<int> global_ptr;
void thread1()
{
while (true)
{
shared_ptr<int> temp;
temp = global_ptr;
if (1 != (*temp)) cout << "Error \n";
}
}
int main(void)
{
global_ptr = shared_ptr<int>(new int(1));
thread th(thread1);
while (true)
{
shared_ptr<int> _newP = shared_ptr<int>(new int(1));
global_ptr = _newP;
}
system("pause");
return 0;
}
왜 이런 현상이 발생하는지 알아보면,
thread1 에서
shared_ptr<int> temp;
temp = global_ptr;
바로 이구간, temp에 글로벌 shared_ptr을 넣는 부분에서 컨텍스트 스위칭이 일어나며 mainThread로 옮겨져
eb_shared_ptr<int> _newP = eb_shared_ptr<int>(new int(1));
global_ptr = _newP;
이 코드가 실행되었다면, thread1에 가지고있는 temp는 값이 사라지게 됩니다.
따라서 Error텍스트가 노출되고, 스코프를 벗어나며 해제된 메모리에 접근하여 _refCount를 가져와서 크래시까지 발생하게 되는 것입니다.
이 방법을 해결하기 위해서는 두가지 방법이 있습니다.
1. atomic_load를 사용한다.
atomic_load를 사용하면 깔끔한 구현이 만들어집니다.
shared_ptr의 생성과 소멸이 잦은 경우가 아니라면 성능에 큰 지장이 있지도 않을 것 같습니다.
atomic_load를 살펴보면 내부에 전역 spin lock이 구현되어 있습니다.
atomic_load을 사용하는 객체는 타입이 다른 shraed_ptr인 경우에도 모두 단 한개의 lock만을 이용하게 될 것입니다. 생성과 소멸이 매우 잦은 경우라면 문제가 생길수도 있다고 생각합니다.
단점이 있다면 atomic_load를 강제할 수 있도록 만들어 놓지 않는다면 빼먹고 그냥 make_shared를 사용하여 로드를 할 수도 있겠죠..
2. c++20에 있는 atomic_shared_ptr을 사용한다.
c++20에서는 atomic_shared_ptr을 사용할 수 있습니다.
이 atomic_shared_ptr은 LockFree로 구현되어 있어 성능상 매우 좋을것으로 생각됩니다.
'운영체제 & 병렬처리 > Multithread' 카테고리의 다른 글
Stomp allocator, STL allocator - VirtualAlloc (0) | 2022.10.08 |
---|---|
멀티스레드에서 shared_ptr 사용시 주의사항 (0) | 2022.10.08 |
Prime number 구하기 (multi thread) (0) | 2022.10.03 |
DFS Deadlock Detection (0) | 2022.10.02 |
Reader-Writer lock (0) | 2022.10.02 |