std :: shared_ptr 스레드 안전성 설명


106

http://gcc.gnu.org/onlinedocs/libstdc++/manual/shared_ptr.html을 읽고 있으며 일부 스레드 안전 문제가 여전히 명확하지 않습니다.

  1. 표준은 참조 카운팅이 스레드로부터 안전하게 처리되고 플랫폼 독립적임을 보장합니다.
  2. 비슷한 문제-표준은 하나의 스레드 (마지막 참조 보유) 만 공유 객체에서 삭제를 호출하도록 보장합니다.
  3. shared_ptr은 그 안에 저장된 객체에 대한 스레드 안전을 보장하지 않습니까?

편집하다:

의사 코드 :

// Thread I
shared_ptr<A> a (new A (1));

// Thread II
shared_ptr<A> b (a);

// Thread III
shared_ptr<A> c (a);

// Thread IV
shared_ptr<A> d (a);

d.reset (new A (10));

스레드 IV에서 reset ()을 호출하면 첫 번째 스레드에서 생성 된 A 클래스의 이전 인스턴스가 삭제되고 새 인스턴스로 대체됩니까? 또한 IV 스레드에서 reset ()을 호출하면 다른 스레드가 새로 생성 된 객체 만 볼 수 있습니까?


24
오른쪽, 오른쪽, 오른쪽.
spraff

16
make_shared대신 사용해야 합니다new
qdii

답변:


87

다른 사람들이 지적했듯이 원래의 3 가지 질문에 대해 올바르게 파악했습니다.

하지만 편집의 마지막 부분은

스레드 IV에서 reset ()을 호출하면 첫 번째 스레드에서 생성 된 A 클래스의 이전 인스턴스가 삭제되고 새 인스턴스로 대체됩니까? 또한 IV 스레드에서 reset ()을 호출하면 다른 스레드가 새로 생성 된 객체 만 볼 수 있습니까?

부정확하다. 만 d새로운 가리 킵니다 A(10)하고 a, b그리고 c원래의 지점으로 계속됩니다 A(1). 이것은 다음의 짧은 예에서 명확하게 볼 수 있습니다.

#include <memory>
#include <iostream>
using namespace std;

struct A
{
  int a;
  A(int a) : a(a) {}
};

int main(int argc, char **argv)
{
  shared_ptr<A> a(new A(1));
  shared_ptr<A> b(a), c(a), d(a);

  cout << "a: " << a->a << "\tb: " << b->a
     << "\tc: " << c->a << "\td: " << d->a << endl;

  d.reset(new A(10));

  cout << "a: " << a->a << "\tb: " << b->a
     << "\tc: " << c->a << "\td: " << d->a << endl;
                                                                                                                 
  return 0;                                                                                                          
}

(분명히, 나는 어떤 스레딩도 신경 쓰지 않았습니다. 그것은 shared_ptr::reset()행동에 영향을 미치지 않습니다 .)

이 코드의 출력은 다음과 같습니다.

a : 1 b : 1 c : 1 d : 1

a : 1 b : 1 c : 1 d : 10


35
  1. 맞습니다. shared_ptrs는 참조 카운트 값의 원자 적 증가 / 감소를 사용합니다.

  2. 표준은 하나의 스레드 만 공유 객체에서 삭제 연산자를 호출하도록 보장합니다. 공유 포인터의 복사본을 삭제하는 마지막 스레드가 delete를 호출하는 스레드가 될 것임을 구체적으로 지정했는지 확실하지 않습니다 (실제로이 경우 일 가능성이 높습니다).

  3. 그렇지 않습니다. 저장된 객체는 여러 스레드에서 동시에 편집 할 수 있습니다.

편집 : 약간의 후속 조치, 일반적으로 공유 포인터가 작동하는 방식에 대한 아이디어를 얻으려면 http://www.boost.org/doc/libs/1_37_0/boost/shared_ptr.hppboost::shared_ptr 소스를 참조하십시오 .


3
1. " 'shared_ptrs'라고 말하면 참조 카운트 값의 원자 적 증가 / 감소를 사용하십시오." 원자 적 증가 / 감소를 위해 내부 잠금을 사용하지 않는다는 의미입니까? 컨텍스트가 전환됩니까? 간단한 언어로 여러 스레드가 잠금을 사용하지 않고 참조 횟수를 증가 / 감소시킬 수 있습니까? 원자 증가는 특별한 atomic_test_and_swap / atomic_test_and_increment 명령에 의해 수행됩니까?
rahul.deshmukhpatil

@rahul 컴파일러는 뮤텍스 / 잠금을 자유롭게 사용할 수 있지만 대부분의 좋은 컴파일러는 잠금없이 수행 할 수있는 플랫폼에서 뮤텍스 / 잠금을 사용하지 않습니다.
Bernard

@Bernard : 플랫폼에 대한 "컴파일러 std lib shared_ptr"구현에 의존한다는 뜻입니까?
rahul.deshmukhpatil

2
예. 내 이해에서 표준은 잠금 장치가 없어야한다고 말하지 않습니다. 그러나 최신 GCC 및 MSVC에서는 Intel x86 하드웨어에서 잠금이 없으며 하드웨어가 지원할 때 다른 좋은 컴파일러도 똑같이 할 수 있다고 생각합니다.
Bernard

18

std::shared_ptr 스레드로부터 안전하지 않습니다.

공유 포인터는 두 개의 포인터 쌍으로, 하나는 객체에 대한 것이고 다른 하나는 제어 블록에 대한 것입니다 (참조 카운터 보유, 약한 포인터에 대한 링크 ...).

std :: shared_ptr이 여러 개있을 수 있으며 참조 카운터를 변경하기 위해 제어 블록에 액세스 할 때마다 스레드로부터 안전하지만 그 std::shared_ptr자체는 스레드로부터 안전하거나 원 자성이 아닙니다.

std::shared_ptr다른 스레드가 사용 하는 동안 새 개체를 할당 하면 새 개체 포인터로 끝날 수 있지만 여전히 이전 개체 => CRASH의 제어 블록에 대한 포인터를 사용합니다.


4
단일 std::shared_ptr인스턴스는 스레드로부터 안전하지 않다고 말할 수 있습니다. std :: shared_ptr 참조에서 :If multiple threads of execution access the same shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur;
JKovalsky

이것은 더 잘 표현 될 수 있습니다. std::shared_ptr<T>항상 사용하는 경우 인스턴스는 스레드 안전 보장 값으로 스레드 경계를 넘어 (복사 / 이동). 다른 모든 사용 std::shared_ptr<T>&은 스레드 경계를 넘어 안전하지
않습니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.