분명 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로 채워진 이미 초기화 된 변수가 나타났다. 좀처럼 이해하기 힘든 동작이였다.
대입 / 치환을 제외하고는 안전하다는 이야기 일 겁니다.
not safe ————————-
g = {}
g = data
g = reset()
g = reset(data)
g.swap()
인터넷에 관련 자료 찾기 힘든데 shared_ptr 과 친구인 weak_ptr 에 대한 내용은 있어서 알려드립니다. 아마 비슷 할 거예요 답변 중 하단에 표로 적어 놓은 부분이 있음
weak_ptr 에 관한 thread-safty 에 대한 내용들이 있던 와중 글을 정리 된것
For brevity in the following discussion, different weak_ptrs and shared_ptrs that all are generated from the same original shared_ptr or unique_ptr will be termed 'instances'. weak_ptrs and shared_ptrs that don't share the same object do not need to be considered in this analysis. The general rules for assessing thread safety are:
The cppreference discussion of std::atomic(std::weak_ptr) is clearest on the safety of simultaneous accesses to different instances: C++20 introduces a std::atomic specialization of weak pointer that provides thread-safe modification of the same instance through appropriate synchronization. Note that when it comes to constructors, initialization from another instance is not atomic. For example, atomic<weak_ptr<T>> myptr(anotherWeakPtr); is not an atomic operation. |
아래 표에서 observer 와 modifer 부분을 보면 된다
The cppreference discussion of std::atomic(std::weak_ptr) is clearest on the safety of simultaneous accesses to different instances:
Note that the control block used by std::weak_ptr and std::shared_ptr is thread-safe: different non-atomic std::weak_ptr objects can be accessed using mutable operations, such as operator= or reset, simultaneously by multiple threads, even when these instances are copies or otherwise share the same control block internally.
C++20 introduces a std::atomic specialization of weak pointer that provides thread-safe modification of the same instance through appropriate synchronization. Note that when it comes to constructors, initialization from another instance is not atomic. For example, atomic<weak_ptr<T>> myptr(anotherWeakPtr); is not an atomic operation.
ref :https://stackoverflow.com/questions/20705304/about-thread-safety-of-weak-ptr
'운영체제 & 병렬처리 > Multithread' 카테고리의 다른 글
compare_exchange_weak, compare_exchange_strong 차이, Spurious wakeup(가짜 기상) (0) | 2022.09.23 |
---|---|
shared_ptr 로 구현한 lock-free 은 lock-free 가 아니다 (3) (0) | 2022.09.22 |
LockFree - Stack (2) - 삭제 처리 (0) | 2022.09.21 |
LockFree - Stack (1) (0) | 2022.09.15 |
LockBase Queue (0) | 2022.09.14 |