문제

아무도 완전히 스레드 안전을 알고 있습니까? shared_ptr 구현? 예를 들어 부스트 구현 shared_ptr 대상 (Refcounting)의 스레드 안전이며 동시에 안전합니다. shared_ptr 인스턴스는 읽지 않지만 쓰기 또는 읽기/쓰기에 대해서는 읽지 않습니다.

(보다 문서를 부스트하십시오, 예 3, 4 및 5).

Shared_ptr 구현이 완전히 안전한 구현이 있습니까? shared_ptr 인스턴스?

Docs를 부스트하는 이상한 말은 다음과 같이 말합니다.

shared_ptr 객체는 내장 유형과 동일한 수준의 스레드 안전성을 제공합니다.

그러나 일반 포인터 (내장 유형)를 smart_ptr, 그런 다음 평범한 포인터의 동시 쓰기는 스레드 안전하지만 동시에 smart_ptr 그렇지 않습니다.

편집 : X86 아키텍처에서의 잠금 장치 구현을 의미합니다.

EDIT2 : 이러한 스마트 포인터의 예제 사용 사례는 현재 작업 항목과 함께 글로벌 Shared_PTR을 업데이트하는 여러 작업자 스레드와 작업 항목의 임의의 샘플을 취하는 모니터 스레드가있는 곳입니다. Shared-PTR은 다른 작업 항목 포인터가 할당 될 때까지 작업 항목을 소유합니다 (이전 작업 항목을 파괴). 모니터는 작업 품목의 소유권을 얻습니다 (따라서 작업 품목이 파괴되지 않음). XCHG 및 수동 삭제로 수행 할 수 있지만 공유 -PTR이 수행 할 수 있다면 좋을 것입니다.

또 다른 예는 Global Shared-PTR이 "프로세서"를 보유하고 일부 스레드에 의해 지정되고 다른 스레드가 사용하는 곳입니다. "사용자"스레드는 프로세서 Shard-PTR이 NULL임을 알면 일부 대체 로직을 사용하여 처리를 수행합니다. NULL이 아닌 경우 프로세서가 자체 공유 -PTR에 할당하여 프로세서가 파괴되는 것을 방지합니다.

도움이 되었습니까?

해결책

이러한 완전한 스레드 -SAFE Shared_PTR 구현에 필요한 장벽을 추가하면 성능에 영향을 줄 수 있습니다. 다음 경주를 고려하십시오 (참고 : 의사 코드가 풍부합니다) :

스레드 1 : global_ptr = a;

스레드 2 : global_ptr = b;

스레드 3 : local_ptr = global_ptr;

우리가 이것을 구성 작업으로 분류하면 :

스레드 1 :

A.refcnt++;
tmp_ptr = exchange(global_ptr, A);
if (!--tmp_ptr.refcnt) delete tmp_ptr;

스레드 2 :

B.refcnt++;
tmp_ptr = exchange(global_ptr, B);
if (!--tmp_ptr.refcnt) delete tmp_ptr;

스레드 3 :

local_ptr = global_ptr;
local_ptr.refcnt++;

분명히, A의 스왑 후 스레드 3이 포인터를 읽으면 B가 진행되어 참조 수를 증가시키기 전에 삭제하면 나쁜 일이 발생합니다.

이를 처리하려면 스레드 3이 refcnt 업데이트를 수행하는 동안 더미 값을 사용해야합니다. 성공적으로 그렇게했다면)

스레드 1 :

A.refcnt++;
tmp_ptr = global_ptr;
while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, A))
    tmp_ptr = global_ptr;
if (!--tmp_ptr.refcnt) delete tmp_ptr;

스레드 2 :

B.refcnt++;
while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, A))
    tmp_ptr = global_ptr;
if (!--tmp_ptr.refcnt) delete tmp_ptr;

스레드 3 :

tmp_ptr = global_ptr;
while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, BAD_PTR))
    tmp_ptr = global_ptr;
local_ptr = tmp_ptr;
local_ptr.refcnt++;
global_ptr = tmp_ptr;

당신은 이제 / 읽기 / 작동의 중간에 원자력이있는 루프를 추가해야했습니다. 이것은 좋은 일이 아닙니다. 일부 CPU에서는 매우 비쌀 수 있습니다. 더구나, 당신도 바쁘고 기다리고 있습니다. 당신은 futexes와 무엇을 위해 영리하게 시작될 수 있습니다. 그러나 그 시점까지 당신은 자물쇠를 재창조했습니다.

모든 운영에 의해 부담 해야하는이 비용은 자연적으로 자물쇠가 당신에게 줄 수있는 것과 매우 유사합니다. 일반적으로 그러한 스레드 -Safe shared_ptr 구현을 보지 못하는 이유입니다. 그런 것이 필요한 경우, 잠금 장치를 자동화하기 위해 Mutex와 Shared_ptr을 편의 클래스로 감싸는 것이 좋습니다.

다른 팁

내장 포인터에 동시 쓰기는 확실히 스레드 안전하지 않습니다. 실제로 자신을 미치게하려면 메모리 장벽과 관련하여 같은 가치에 대한 글쓰기의 의미를 고려하십시오 (예 : 동일한 포인터가 다른 값을 가지고 있다고 생각할 수 있습니다).

RE : COMMING- 내장이 더블 삭제되지 않은 이유는 전혀 삭제되지 않기 때문입니다 (Boost :: Shared_PTR의 구현은 특별한 원자 증분 및 감소를 사용하기 때문에 두 번 삭제되지 않습니다. 따라서 단일 삭제 만 할 것이지만 결과는 하나의 포인터와 다른 하나의 참고 문헌을 가질 수 있습니다. 부스트 문서의 진술은 올바른 것이므로 내장 된 것과 동일한 보증을받습니다.

Re : edit2- 당신이 설명하는 첫 번째 상황은 내장과 Shared_ptrs 사용간에 매우 다릅니다. 하나 (XCHG 및 수동 삭제)에는 참조 수가 없습니다. 당신은 당신이 이것을 할 때 당신이 유일한 소유자라고 가정합니다. 공유 포인터를 사용하는 경우 다른 스레드에 소유권이있을 수 있다고 말하면서 상황이 훨씬 더 복잡해집니다. 나는 그것이 비교와 스웨이로 가능하다고 생각하지만, 이것은 매우 포송 할 수 없을 것입니다.

C ++ 0X에는 Atomics 라이브러리가 나오고 있으며 일반적인 멀티 스레드 코드를 훨씬 쉽게 작성할 수 있습니다. 스레드 안전 스마트 포인터의 좋은 크로스 플랫폼 참조 구현을보기 위해 나올 때까지 기다려야 할 것입니다.

나는 그런 스마트 포인터 구현을 모른다. 그러나 나는이 행동이 어떻게 유용 할 수 있습니까? 동시 포인터 업데이트를 찾을 수있는 곳에서 생각할 수있는 유일한 시나리오는 레이스 조건 (즉, 버그)입니다.

이것은 비판이 아닙니다. 합법적 인 사용 사례가있을 수 있습니다. 나는 단지 그것을 생각할 수 없습니다. 알려주세요!

Re : EDIT2 몇 가지 시나리오를 제공해 주셔서 감사합니다. 원자 포인터 쓰기가 그러한 상황에서 유용한 것처럼 들립니다. (한 가지 작은 것 : 두 번째 예제에서, 당신이 "null이 아닌 경우, 프로세서가 자체 공유 -PTR에 할당하여 파괴되는 것을 방지 할 때"글로벌 공유 포인터를 로컬 공유 포인터 먼저 그 다음에 로컬 공유 포인터가 Null인지 확인하십시오. 설명하는 방식은 테스트 후 및 로컬 제품에 할당하기 전에 글로벌 공유 포인터가 무효화되는 경주 조건에 나타납니다.)

이 구현을 사용할 수 있습니다 원자 기준 계수 포인터 최소한 참조 계산 메커니즘을 구현합니다.

컴파일러는 이미 최신 C ++ 표준에서 스레드 안전 스마트 포인터를 제공 할 수 있습니다. 나는 믿는다 TBB 스마트 포인터를 추가 할 계획이지만 아직 포함되어 있다고 생각하지 않습니다. 그러나 TBB의 스레드 안전 컨테이너 중 하나를 사용할 수 있습니다.

모든 공유 포인터가있는 Mutex 객체를 포함시키고 잠금 장치로 득점/감소 명령을 포장하여 쉽게 수행 할 수 있습니다.

나는 이것이 그렇게 쉽지 않다고 생각하지 않습니다. SH_PTR 클래스를 CS로 랩핑하는 것만으로는 충분하지 않습니다. 모든 공유 포인터에 대해 하나의 단일 CS를 유지하면 서로 다른 스레드간에 SH_PTR 객체의 액세스 및 삭제를 피할 수 있습니다. 그러나 이것은 끔찍할 것입니다. 모든 공유 포인터에 대한 CS 객체 하나는 실제 병목 현상이 될 것입니다. 모든 랩핑 가능한 새로운 PTR -S가 다른 CS S '를 가지고 있다면 적합하지만이 방법으로 우리는 CS Dinamically를 만들고 SH_PTR 클래스의 사본 CTOR 가이 공유 CS를 전송하도록해야합니다. 이제 우리는 같은 문제에 도달했습니다. 인스턴스 당 휘발성 M_BRELEASED 플래그로 조금 더 똑똑 할 수 있지만이 방법으로 플래그 점검과 공유 CS를 사용하는 것 사이의 안전 간격을 고정시킬 수는 없습니다. 이 문제에 대해 완전히 안전한 해결책을 볼 수 없습니다. 어쩌면 그 끔찍한 글로벌 CS는 앱을 죽이는 것만 큼 사소한 나쁜 일이 될 것입니다. (내 영어로 죄송합니다)

이것은 정확히 당신이 원하는 것이 아니라 boost::atomic 문서화는 원자 카운터를 사용하는 방법에 대한 예를 제공합니다. intrusive_ptr. intrusive_ptr 부스트 스마트 포인터 중 하나이며, "침입 참조 계산"을 수행합니다. 즉, 카운터가 스마트 포인터에 의해 제공되는 대신 대상에 "내장 된"것을 의미합니다.

후원 atomic 사용 예제 :

http://www.boost.org/doc/html/atomic/usage_examples.html

제 생각에 가장 쉬운 해결책은 intrusive_ptr 사소한 (그러나 필요한) 수정으로.

아래 구현을 공유했습니다.

http://www.philten.com/boost-smartptr-mt/

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top