2017-01-06 12 views
6
std::shared_ptr<int> int_ptr; 

int main() { 
    int_ptr = std::make_shared<int>(1); 
    std::thread th{[&]() { 
     std::weak_ptr int_ptr_weak = int_ptr; 
     auto int_ptr_local = int_ptr_weak.lock(); 
     if (int_ptr_local) { 
      cout << "Value in the shared_ptr is " << *int_ptr_local << endl; 
     } 
    }); 

    int_ptr.reset(nullptr); 
    th.join(); 
    return 0; 
} 

위 코드는 스레드 안전합니까? 나는이 대답 About thread-safety of weak_ptr을 읽었지만, 위의 코드가 쓰레드에 안전한지 확인하고 싶었다.어떻게`weak_ptr` 및`shared_ptr` 액세스가 원자 단위입니까?

위의 코드가 실제로 스레드 안전 인 경우 std::weak_ptrstd::shared_ptr 인터페이스가 다음 작업을 원자 단위로 처리하는 방법을 이해할 수 없음 expired() ? shared_ptr<T>() : shared_ptr<T>(*this). 위와 같은 두 개의 논리적 인 코드 라인을 만드는 것은 일종의 뮤텍스 나 스핀 록을 사용하지 않고는 동기화할 수 없다는 것입니다.

원자 증분이 공유 포인터의 다른 인스턴스에서 작동하는 방식을 알고 있고 shared_ptr 자체가 스레드 안전하지 않다는 것을 이해합니다. 그러나 위의 스레드가 실제로 안전 할 경우 스레드 안전과 매우 비슷합니다. shared_ptr과는 다릅니다. 위의 조건 에서처럼 두 줄의 코드를 자물쇠없이 원자 단위로 만들 수있는 방법을 이해합니다.

+0

* "매우 안전한 스레드입니다. shared_ptr ''* - 공유 된''shared_ptr'을 사용하여''weak_ptr''을 쓰레드로부터 안전하게 만드십니까? – Holt

+0

구현이 mutex를 사용하여 'lock'원자를 만드는 것을 멈추게하는 것은 아무것도 없습니다. 그러나 매우 효율적이지는 않을 것입니다. – Oktalist

답변

2

이 질문은 두 부분이 있습니다

스레드 안전성 코드는하지 스레드 입니다

을, 그러나 이것은 lock()와는 아무 상관이 없습니다 :
레이스가 int_ptr.reset();std::weak_ptr int_ptr_weak = int_ptr; 사이에 존재합니다.하나의 스레드가 비 원자 변수 int_ptr을 수정하는 동안 다른 스레드는이를 읽습니다. 이는 정의에 따라 데이터 경쟁입니다.

그래서이 확인 될 것이다 : 전체 과정이 원자 수 없습니다 물론 예제 코드 expired() ? shared_ptr<T>() : shared_ptr<T>(*this)

int main() { 
    auto int_ptr = std::make_shared<int>(1); 
    std::weak_ptr<int> int_ptr_weak = int_ptr; //create the weak pointer in the original thread 
    std::thread th([&]() { 
     auto int_ptr_local = int_ptr_weak.lock(); 
     if (int_ptr_local) { 
      std::cout << "Value in the shared_ptr is " << *int_ptr_local << std::endl; 
     } 
    }); 

    int_ptr.reset(); 
    th.join(); 
} 

원자 버전. 실제로 중요한 부분은 이미 심한 ref 카운트가 이미 0보다 크고 체크와 증가가 원자적인 방식으로 발생하는 경우에만 증가됩니다. 이 시스템에 사용할 수있는 시스템/아키텍처 관련 프리미티브가 있는지는 모르겠지만 C++ 11에서 구현하는 한 가지 방법은 다음과 같습니다.

std::shared_ptr<T> lock() { 
    if (!isInitialized) { 
     return std::shared_ptr<T>(); 
    } 
    std::atomic<int>& strong_ref_cnt = get_strong_ref_cnt_var_from_control_block(); 
    int old_cnt = strong_ref_cnt.load(); 
    while (old_cnt && !strong_ref_cnt.compare_exchange_weak(old_cnt, old_cnt + 1)) { 
     ; 
    } 
    if (old_cnt > 0) { 
     // create shared_ptr without touching the control block any further 
    } else { 
     // create empty shared_ptr 
    } 
} 
+0

답변 해 주셔서 감사합니다! 내가 여기에서 혼란 스러웠던 부분은'lock()'을 수행하는 방법은 자물쇠 (예를 들어 스핀 락)를 포함한다는 것이다. 표준에서'lock()'함수는 자물쇠가없는 방식으로 수행하십시오. 그건 사실이 아닌가? – Curious

+0

@Curious : 어디에서 작업을 잠금 해제해야한다는 인상을 받았습니까? 이 표준은 원자 적으로 발생해야한다고 말하면서 잠금 장치가없는 것과 동일하지 않습니다. 사실, std :: atomic_flag에 대한 작업은 표준에서 잠금이 필요없는 유일한 작업입니다. 또한 이걸 스핀 락이라고 부르면 확실하지 않습니다. 코드가 여기에서 아무것도 얻지도, 릴리스하지도 기다리지도 않기 때문에 – MikeMB

1

인터페이스들하지 않는다 "는 std::weak_ptrstd::shared_ptr 인터페이스는 원자 expired() ? shared_ptr<T>() : shared_ptr<T>(*this)는 다음과 같은 작업을 할 방법"을 참조하십시오. 구현에 내부적입니다. 실행 방법은 구현마다 다를 수 있습니다.

+0

난 그 코드 줄이 자물쇠없이 원자 일 수있는 방법을 이해하지 못했다. 그러한 구현 중 하나의 예를 들려 줄 수 있습니까? – Curious

+1

@Curious : 코드 줄은 _ 원자가 아닙니다. 구현에는 다른 코드가 있습니다. 템플릿이기 때문에 구현시 예제 만 찾을 수 있습니다. – MSalters

4

위 코드는 스레드로부터 안전한가요? int_ptr.reset(nullptr);으로 내가 아니에요 생각

는, 나는 표준 : : weak_ptr를 및 표준 : : shared_ptr의 인터페이스는 원자 다음 조작을 어떻게 이해할 수없는 std::weak_ptr int_ptr_weak = int_ptr;

에 대해 경주 expired() ? shared_ptr<T>() : shared_ptr<T>(*this)

이러한 작업은 원자가 아닙니다. expired()이 false를 반환 할 수 있지만 해당 값을 처리 할 때 더 이상 정확하지 않을 수 있습니다. 반면에 true를 반환하면 그 이후로 이 특정 인스턴스를 수정하지 않는 한 정확한 결과가 보장됩니다. 즉, 주어진 shared_ptr의 다른 사본에 대한 조작으로 인해 만료되지 않을 수 있습니다.

weak_ptr::lock() 구현은 expired()을 사용하지 않을 예정입니다. 원자 비교 - 교환과 같은 일을 할 것입니다. 여기서 강한 참조는 현재 숫자가 0보다 큰 경우에만 추가됩니다.

+0

코드 라인이 원자 적이라고 cppreference가 말했기 때문에 나는 물었다. http://en.cppreference.com/w/cpp/memory/weak_ptr/lock 내가 잘못 해석 했습니까? – Curious

+1

cppreference.com의 문구는 다음과 같습니다 : _ 만료 됨()? shared_ptr () : shared_ptr (* this), 원자 적으로 실행 _. 그것은 "그 효과가 있지만 원자 적으로 효과가있는 것"이라고 해석해야합니다. –

+0

그렇다면 코드는 스레드로부터 안전합니까? – Curious

3

아니요, 코드가 스레드로부터 안전하지 않습니다. 주 스레드 (쓰기 작업)의 int_ptr.reset() 작업과 int_weak_ptr의 초기화는 int_ptr에서 th (읽기 작업 임)입니다.

+0

그러나 cppreference는 'lock' 메소드가 자신의 로직을 원자 적으로 실행한다고 말합니다. http://en.cppreference.com/w/cpp/memory/weak_ptr/lock – Curious

+2

@Curious이 답변은'lock'에 대해서는 언급하지 않았습니다. – Oktalist