분명 shared_ptr은 레퍼런스 카운트가 스레드 안정적이라고 되어있다고 나와있지만 아래와같은 코드를 실행하면 실행하자마자 에러가 발생한다.
#include <memory> std::shared_ptr<int> g; DWORD WINAPI U(void *) { while (true) { std::shared_ptr<int> data = g; } return 0; } DWORD WINAPI C(void *) { while (true) { std::shared_ptr<int> data = std::make_shared<int>(0); g = data; } return 0; } int main() { HANDLE h1 = CreateThread(NULL, 0, U, NULL, 0, NULL); HANDLE h2 = CreateThread(NULL, 0, C, NULL, 0, NULL); WaitForSingleObject(h1, -1); WaitForSingleObject(h2, -1); return 0; }
거의 수시간동안 구글링해도 한결같이 나오는건 레퍼런스 카운트는 스레드 안전하다는 것 정도.
그래도 다행이도 계속 검색해보니 결국 아래와같이 atomic을 사용하는 코드가 나왔다.
#include <atomic> #include <memory> std::shared_ptr<int> g; DWORD WINAPI U(void *) { while (true) { std::shared_ptr<int> data = std::atomic_load(&g); } return 0; } DWORD WINAPI C(void *) { while (true) { std::shared_ptr<int> data = std::make_shared<int>(0); std::atomic_store(&g, data); } return 0; } int main() { HANDLE h1 = CreateThread(NULL, 0, U, NULL, 0, NULL); HANDLE h2 = CreateThread(NULL, 0, C, NULL, 0, NULL); WaitForSingleObject(h1, -1); WaitForSingleObject(h2, -1); return 0; }
저렇게 atomic_store, atomic_load를 사용하니 이제 정상적으로 shared_ptr 객체가 두 스레드간의 참조가 문제없이 되었다. 분명 부스트의 shared_ptr 기준으로 코드를 보면 InterlockedIncrement 함수를 사용하여 카운트하던데 그마저도 저런식으로 사용하니 0xDDDDDDDD로 채워진 이미 초기화 된 변수가 나타났다. 좀처럼 이해하기 힘든 동작이였다.
수시간, 그리고 며칠동안 구글링 아무리 해봐도 shared_ptr는 참조 카운트가 스레드 안전하다고 할 뿐 좀 더 자세한 내용 잘 나오지도않고(…) 그냥 이렇게 사용해야 정말 안전하다 정도로 알고 넘어가야겠다 -_-;;;
포크
대입 / 치환을 제외하고는 안전하다는 이야기 일 겁니다.
not safe ————————-
g = {}
g = data
g = reset()
g = reset(data)
g.swap()
인터넷에 관련 자료 찾기 힘든데 shared_ptr 과 친구인 weak_ptr 에 대한 내용은 있어서 알려드립니다. 아마 비슷 할 거예요 답변 중 하단에 표로 적어 놓은 부분이 있음
https://stackoverflow.com/questions/20705304/about-thread-safety-of-weak-ptr
iruis
의외로 찾기 힘들고 찾지 못했던 정보군요. 위에 atomic을 사용한것처럼 일단 치환이나 대입되는 부분만큼은 스레드 동기화 꼭 해줘야겠음을 다시 한번 느낍니다(…).