2016-07-12 3 views
0

답변과 비슷한 질문이 많이 있지만이 특정 사례를 아직 이해할 수 없기 때문에 질문을하기로 결정했습니다.표준 : std :: shared_ptr의 스레드 안전성

내가 가지고있는 것은 동적으로 할당 된 배열 (MyVector)에 대한 shared_ptrs의 맵입니다. 내가 원하는 것은 잠글 필요없이 제한된 동시 액세스입니다. 나는 그 자체지도는 스레드로부터 안전하지 않습니다 것을 알고,하지만 난 항상이다, 내가 확인을해야 여기에 뭘하는지 생각 :

typedef shared_ptr<MyVector<float>> MyVectorPtr; 

for (int i = 0; i < numElements; i++) 
{ 
    content[i] = MyVectorPtr(new MyVector<float>(numRows)); 
} 
:

그와 같은 단일 스레드 환경에서 맵을 채우기

초기화 후 요소에서 읽는 하나의 스레드와 shared_ptrs가 가리키는 스레드를 대체하는 스레드가 있습니다.

스레드 1 :

for(auto i=content.begin();i!=content.end();i++) 
{ 
    MyVectorPtr p(i->second); 
    if (p) 
    { 
     memory_use+=sizeof(int) + sizeof(float) * p->number; 
    } 
} 

스레드 2 : 내가 할 동안 독방 감금 오류 또는 두 개의 스레드 중 하나에서 두 번 무료로 하나 후

for (auto itr=content.begin();content.end()!=itr;++itr) 
    { 
     itr->second.reset(new MyVector<float>(numRows)); 
    } 

. 어떻게 든 놀랍지도 않지만 실제로는 얻지 못합니다.

  1. I 추가하거나 다중 스레드 환경에서지도의 모든 항목을 제거하지 않으므로, 반복자는 항상 올바른 일을 가리켜 야 :

    나는이 일 것이라고 생각하는 이유는있다 .

  2. 지도의 단일 요소를 동시에 변경하는 것은 조작이 원자적일 때만 괜찮다고 생각했습니다.
  3. 나는 shared_ptr (스레드 1에서 스레드 수를 늘리거나 감소시키는 ref 수를 늘리거나, 스레드 2에서 다시 설정)에서 수행하는 작업이 원자 적이라고 생각했습니다. SO Question

물론, 내 가정 중 하나 이상이 잘못되었거나 내가 생각하는대로하지 않습니다. 내 생각에 리셋은 실제로 쓰레드에 안전하지 않다. std :: atomic_exchange help?

나를 풀어줄 수 있습니까? 고마워요! 누군가가 시도하고자하는 경우

, 여기에 전체 코드의 예는 다음과 같습니다

#include <stdio.h> 
#include <iostream> 
#include <string> 
#include <map> 
#include <unistd.h> 
#include <pthread.h> 


using namespace std; 

template<class T> 
class MyVector 
{ 
public: 
    MyVector(int length) 
    : number(length) 
    , array(new T[length]) 
    { 
    } 

    ~MyVector() 
    { 
     if (array != NULL) 
     { 
      delete[] array; 
     } 
     array = NULL; 
    } 

    int number; 

private: 
    T* array; 
}; 

typedef shared_ptr<MyVector<float>> MyVectorPtr; 


static map<int,MyVectorPtr> content; 
const int numRows = 1000; 
const int numElements = 10; 

//pthread_mutex_t write_lock; 

double get_cache_size_in_megabyte() 
{ 
    double memory_use=0; 
    //BlockingLockGuard guard(write_lock); 
    for(auto i=content.begin();i!=content.end();i++) 
    { 
     MyVectorPtr p(i->second); 
     if (p) 
     { 
      memory_use+=sizeof(int) + sizeof(float) * p->number; 
     } 
    } 

    return memory_use/(1024.0*1024.0); 

} 


void* write_content(void*) 
{ 
    while(true) 
    { 
     //BlockingLockGuard guard(write_lock); 
     for (auto itr=content.begin();content.end()!=itr;++itr) 
     { 
      itr->second.reset(new MyVector<float>(numRows)); 
      cout << "one new written" <<endl; 
     } 

    } 
    return NULL; 
} 

void* loop_size_checker(void*) 
{ 
    while (true) 
    { 
     cout << get_cache_size_in_megabyte() << endl;; 
    } 
    return NULL; 
} 

int main(int argc, const char* argv[]) 
{ 
    for (int i = 0; i < numElements; i++) 
    { 
     content[i] = MyVectorPtr(new MyVector<float>(numRows)); 
    } 

    pthread_attr_t attr; 
    pthread_attr_init(&attr) ; 
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 
    pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); 

    pthread_t *grid_proc3 = new pthread_t; 
    pthread_create(grid_proc3, &attr, &loop_size_checker,NULL); 

    pthread_t *grid_proc = new pthread_t; 
    pthread_create(grid_proc, &attr, &write_content,(void*)NULL); 


    // to keep alive and avoid content being deleted 
    sleep(10000); 
} 
+3

'MyVector'에 대한 3/5/0의 규칙 ... (그리고 왜'std :: vector'를 사용하지 않겠습니까?) – Jarod42

+1

'if (array! = NULL)'- NULL을 검사 할 필요가 없습니다. 'delete []'를 호출 할 때. – PaulMcKenzie

+0

'shared_ptr :: operator = (const shared_ptr & other)'다른 스레드에서'other'가 지정되면 스레드 안전하지 않습니다. 'std :: atomic >'은 아마도 덜 잠금이 아니기 때문에 자물쇠에 머물고 싶지 않다면 직접 메모리를 관리해야 할 것입니다. – Dani

답변

0

TL; DR;

변경 std::map은 스레드로부터 안전하지 않으며, 참조 용으로는 std::shared_ptr을 사용하는 것이 안전합니다.

예를 들어 다음과 같은 적절한 동기화 메커니즘을 사용하여 읽기/쓰기 작업에 대한지도 액세스를 보호해야합니다. std::mutex.

또한 std::shared_ptr이 참조하는 인스턴스의 상태가 변경되어야하는 경우 동시 스레드에서 액세스하는 경우 데이터 경합으로부터 보호해야합니다.


현재 고객님 께서 보여 주신 것은 MyVector입니다. 너무 단순한 구현 방법입니다.

+0

맞다면지도를 변경하지 않고지도의 std :: shared_ptr 만 변경하므로 작동하지 않는 이유는 무엇입니까? – schluchc

+0

자물쇠가 문제를 해결할 것이라는 것을 알고 있지만, 꼭 필요한 것은 아니지만 자물쇠를 자유롭게하고 싶습니다. – schluchc

2

지도의 한 요소를 동시에 변경하는 것이 조작이 원자 적이라면 괜찮다고 생각했습니다.

std::atomic과 같은 원자형이 없으면지도의 요소를 변경하는 것은 원 자적이 아닙니다.

난 (스레드 2에서 다시 증가 REF 카운트, 스레드 1에서 감량 (REF)의 카운트)이다 I 원자는 shared_ptr의 동작에 어떻게 생각.

맞습니다. 불행히도 기본 포인터도 변경하고 있습니다. 그 포인터는 원자가 아닙니다. 원자가 아니기 때문에 동기화가 필요합니다.

당신이 할 수있는 한 가지는 std::shared_ptr과 함께 도입 된 atomic free functions을 사용하는 것입니다. 이렇게하면 mutex을 사용하지 않아도됩니다.

이 요구 생성자는 다음과 같습니다 다음 underlying shared pointerreference count의 아마 귀결

template< class Y > 
shared_ptr(const shared_ptr<Y>& r) = default; 

2에 할당

+0

어떻게 기본 포인터를 변경하지 않고 shared_ptr에서 재설정을 호출 할 수 있습니까? – schluchc

+0

내가 조사한 원자 함수이지만 @Dani에 따르면 그들은 내 문제를 실제로 해결하지 못합니다. – schluchc

0

는 스레드-1에서 실행중인 MyVectorPtr p(i->second);을 확장 할 수 있습니다.

thread-1에서 포인터가 p에 할당되는 동안 스레드 2가 공유 포인터를 삭제하는 경우가 매우 잘 발생할 수 있습니다. shared_ptr 안에 저장된 기본 포인터는 이 아니고 원자입니다.

따라서 std::shared_ptr의 사용은 스레드로부터 안전하지 않습니다. 기본 포인터를 업데이트하거나 수정하지 않는 한 스레드로부터 안전합니다.