2012-04-22 2 views
18

방해받는 공유 포인터를 쓰고 있으며 참조 카운터에 C++ 11 <atomic> 기능을 사용하고 있습니다. 여기에 내 코드 관련 조각은 다음과 같습니다C++ 11 원자력 및 간섭 공유 포인터 참조 횟수

//... 
mutable std::atomic<unsigned> count; 
//... 

void 
SharedObject::addReference() const 
{ 
    std::atomic_fetch_add_explicit (&count, 1u, 
     std::memory_order_consume); 
} 

void 
SharedObject::removeReference() const 
{ 
    bool destroy; 

    destroy = std::atomic_fetch_sub_explicit (&count, 1u, 
     std::memory_order_consume) == 1; 

    if (destroy) 
     delete this; 
} 

내가 memory_order_acquirememory_order_release 첫째로 시작했지만 그때 memory_order_consume이 충분해야한다고 자신을 설득했다. 더 심의를 마친 후에도 memory_order_relaxed조차도 작동 할 것입니다.

이제는 조작에 memory_order_consume을 사용할 수 있는지 또는 더 약한 주문 (memory_order_relaxed)을 사용할 수 있습니까? 아니면 더 엄격한 주문을 사용해야합니까?

+0

카운터는 기본적으로'delete' 문에 대한 재귀 적 잠금 역할을하기 때문에'removeReference'의'addReference'와'releaseRelease'의 "acquire"는 올바른 순서입니다. 그러나 당신의'addReference'는 카운터가 0이 아닌지 확인해야합니다! –

+0

@KerrekSB : 객체가'SharedPtr <> '에 할당되기 전에 객체가 처음 생성 된 후에 카운터는'addReference()'에서 0이 될 수 있습니다. Acquire/release 의미론은 항상 작동해야합니다. 하지만 더 약한 주문 제약 조건을 사용할 수 없으며 그 이유는 무엇입니까? – wilx

+0

0에 대하여 : refcount가 1이라고 가정하십시오. 이제 스레드 1이 객체를 삭제하려고하고 빼기를 호출합니다. 이 시점에서 스레드 2가 스레드 수를 늘리려고하면 0을 1 씩 증가 시키지만 스레드 1은 계속 진행하여 어쨌든 개체를 삭제합니다. 그것은 피해야합니다. –

답변

20
void 
SharedObject::addReference() const 
{ 
    std::atomic_fetch_add_explicit (&count, 1u, std::memory_order_relaxed); 
} 

void 
SharedObject::removeReference() const 
{ 
    if (std::atomic_fetch_sub_explicit (&count, 1u, std::memory_order_release) == 1) { 
     std::atomic_thread_fence(boost::memory_order_acquire); 
     delete this; 
    } 
} 

당신은 delete 엄격 fetch_sub 후입니다 atomic_thread_fence는 사용하고 싶습니다. 링크 된 텍스트에서 Reference

인용구 :

참조 카운터를 증가는 항상 함께 할 수 memory_order_relaxed : 객체에 대한 새로운 참조는 기존의 참조에서 을 형성하고,에서 기존 기준을 통과 할 수있다 하나의 다른 스레드로의 스레드는 필요한 동기화를 제공해야합니다.

스레드의 기존 개체 참조를 통해 스레드에 개체를 삭제하기 전에 발생할 수있는 모든 액세스를 강제하는 것이 중요합니다. 이것은 참조를 삭제 한 후에 "릴리스" 작업 (이 참조는 분명히 전에 발생 했어야합니다. 을 통해 객체에 액세스) 및 객체를 삭제하기 전에 "취득" 작업에 의해 수행됩니다.

fetch_sub 작업에 memory_order_acq_rel을 사용할 수있을 것이지만, 참조 카운터가 아직 제로에 도달하지 않고 성능 벌금을 부과 할 수 있습니다 때 불필요한 "취득"작업 결과.

+0

나는 이미 대답을 받아들이고 그런 식으로 침입 포인터를 구현했지만 인용문은 나를 조금은 생각하게 만들었고 또 다른 질문이되었다. 감소분을'memory_order_relaxed'로 완화시키고 if()의 실제 분기를 만들기 위해'memory_order_acq_rel'을 사용하는 것은 어떻습니까? – wilx

+0

에'_relaxed', 그 다음에'_acq_rel'을 쓰면'fetch_sub (_relaxed)'와'fence (_acq_rel)'과'delete'가 엄격하게 정렬되지 않습니다. 이 [페이지] (http://www.chaoticmind.net/~hcb/projects/boost.atomic/doc/atomic/thread_coordination.html)에 관심이있을 수 있습니다. – user2k5

+0

좋아, 페이지를 읽은 후에 나는 그것이'fetch_sub (_relaxed)'일 수 없다는 것을 이해한다고 생각한다. 그것은'_relaxed' 연산이 다른 연산들에 대해 정렬되지 않기 때문입니다. 그러나'fetch_sub (_consume)'과'fence (_acq_rel)'은 어떨까요? 'fetch_sub (_consume)'은 컴파일러 재정렬 장벽을 만들고'fence (_acq_rel)'은 나머지 기계에 대한 모든로드와 저장을 명령하고 다음의'delete'는 메모리의 일관된 뷰를 갖습니다. 아니면 내가 틀렸어? – wilx