2017-12-28 29 views
1

하나의 스레드가 큰 버퍼로 메시지를 읽고 스레드를 묶어 처리하는 유스 케이스가 있습니다. 버퍼는 그 이후에 여러 스레드에 의해 공유됩니다. 읽기 전용이며 마지막 스레드가 완료되면 버퍼를 해제해야합니다. 버퍼는 lock-free slab 할당 자로부터 할당된다.boost :: intrusive_ptr을 사용한 공유 버퍼

초기 디자인에서는 버퍼에 shared_ptr을 사용했습니다. 그러나 버퍼 크기가 다를 수 있습니다. 주위를 돌아 다니는 나의 길은 이와 비슷한 것이 었습니다.

SharedBuffer allocate (size_t size) 
{ 
    auto buf = std::allocate_shared<std::array<uint8_t, 16_K>>(myallocator); 
    return SharedBuffer{16_K, buf}; // type erase the std::array 
} 

을 그리고 SharedBuffer 그것을 원하는 각 스레드의 대기열에 포함됩니다 :

struct SharedBuffer { 
    SharedBuffer (uint16_t len, std::shared_ptr<void> ptr) 
       : _length(len), _buf(std::move(ptr)) 
    { 
    } 
    uint8_t data() { return (uint8_t *)_buf.get(); } 
    uint16_t length 
    std::shared_ptr<void> _buf; // type-erase the shared_ptr as the SharedBuffer 
           // need to stored in some other structs 
}; 

이제 할당이 같이있는 shared_ptr을 할당합니다.

이제는 불필요하게 많은 일을하고 있다고 생각합니다. 다음과 같이 boost :: intrusive_ptr을 사용하면됩니다. 상황은 가변적 인 크기의 배열을 사용하기 때문에 조금 C'ish입니다. 여기에서는 단순화를 위해 new() 연산자로 슬랩 할당자를 변경했습니다. 이 구현이 괜찮은지 확인하기 위해 실행하려고했습니다.

template <typename T> 
inline int atomicIncrement (T* t) 
{ 
    return __atomic_add_fetch(&t->_ref, 1, __ATOMIC_ACQUIRE); 
} 

template <typename T> 
inline int atomicDecrement (T* t) 
{ 
    return __atomic_sub_fetch(&t->_ref, 1, __ATOMIC_RELEASE); 
} 

class SharedBuffer { 
public: 

    friend int atomicIncrement<SharedBuffer>(SharedBuffer*); 
    friend int atomicDecrement<SharedBuffer>(SharedBuffer*); 

    SharedBuffer(uint16_t len) : _length(len) {} 

    uint8_t *data() 
    { 
     return &_data[0]; 
    } 

    uint16_t length() const 
    { 
     return _length; 
    } 

private: 

    int    _ref{0}; 
    const uint16_t _length; 
    uint8_t   _data[]; 
}; 

using SharedBufferPtr = boost::intrusive_ptr<SharedBuffer>; 

SharedBufferPtr allocate (size_t size) 
{ 
    // dummy implementation 
    void *p = ::operator new (size + sizeof(SharedBuffer)); 

    // I am not explicitly constructing the array of uint8_t 
    return new (p) SharedBuffer(size); 
} 

void deallocate (SharedBuffer* sbuf) 
{ 
    sbuf->~SharedBuffer(); 

    // dummy implementation 
    ::operator delete ((void *)sbuf); 
} 

void intrusive_ptr_add_ref(SharedBuffer* sbuf) 
{ 
    atomicIncrement(sbuf); 
} 

void intrusive_ptr_release (SharedBuffer* sbuf) 
{ 
    if (atomicDecrement(sbuf) == 0) { 
     deallocate(sbuf); 
    } 
} 

답변

0

나는 (즉, 첫 번째 프로파일) 특정 문제를 방지하지 않는 한 (shared_ptr 사용) 간단한 구현을 사용하십시오.

사이드 참고 : 당신은 [c++20에서 표준 라이브러리에 추가되고있는, boost::make_shared<T[]>(N)boost::shared_pointer<>를 사용할 수 있습니다.

allocate_shared은 사용자가 침입 방식과 마찬가지로 제어 블록을 이미 동일한 할당에 포함하고 있습니다.

마지막으로, 실수로 잘못 사용할 수없는 명확한 계약을 체결 했으므로 마지막으로 std::atomic_int을 사용합니다. 동시에, 그것은 복잡성의 나머지 비트를 제거합니다.