분명 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:

  1. Simultaneous const member function calls on the same instance are thread-safe. All observer functions are const.
  2. Simultaneous calls on different instances are thread-safe, even when one of the calls is a modifier.
  3. Simultaneous calls on the same instance when at least one of the calls is a modifier are not thread-safe.
The following table shows thread-safety when two threads are operating on the same instance at the same time.
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

 

About thread-safety of weak_ptr

std::shared_ptr<int> g_s = std::make_shared<int>(1); void f1() { std::shared_ptr<int>l_s1 = g_s; // read g_s } void f2() { std::shared_ptr<int> l_s2 = std::make_sha...

stackoverflow.com

ref : http://rette.iruis.net/2016/10/shared_ptr-%EA%B0%9D%EC%B2%B4%EB%A5%BC-%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C%EC%97%90%EC%84%9C-%EC%82%AC%EC%9A%A9%ED%95%A0-%EB%95%8C/

반응형

+ Recent posts