2011-01-11 5 views
1

개체의 포인터를 저장하는 정렬되지 않은 맵이 있습니다. thread의 안전성을 유지하기 위해서 올바른 일을하고 있는지 나는 확신 할 수 없다.STL 컨테이너를 사용한 멀티 스레딩

typedef std::unordered_map<string, classA*>MAP1; 
MAP1 map1; 
pthread_mutex_lock(&mutexA) 
    if(map1.find(id) != map1.end()) 
    { 
     pthread_mutex_unlock(&mutexA); //already exist, not adding items 
    } 
    else 
    { 
     classA* obj1 = new classA; 
     map1[id] = obj1; 
     obj1->obtainMutex(); //Should I create a mutex for each object so that I could obtain mutex when I am going to update fields for obj1? 
     pthread_mutex_unlock(&mutexA); //release mutex for unordered_map so that other threads could access other object 
     obj1->field1 = 1; 
     performOperation(obj1); //takes some time 
     obj1->releaseMutex(); //release mutex after updating obj1 
    } 
+2

'-> obtainMutex()'가 생성되기 전에 다른 스레드가 obj1에 도착하는 것이 가능하지 않으면 교착 상태가 발생할 수있는 영역이 표시되지 않습니다. 다시 말해'read '나'write'를 위해'map1'에 접근하기 전에'mutexA'를 잠그는 한 괜찮을 것입니다. 그러나'pthread_mutex_unlock (뮤텍스 A)'를지도의 'id'가 그 안에있는 객체의 내용에 따라 바뀌지 않는 한 한 줄 위로 올리면된다고 생각합니다. – milkypostman

+0

'map1'에 대한 업데이트를 위해 다중 스레드를 통해 액세스되는'map'에 저장된 객체가 있습니까? 객체를 thread-safe하게 만드는 것이 더 좋지 않은가? - 그 말을 유감스럽게 생각한다 - obj1-> obtainMutex'를 사용하는 코드? – Jaywalker

+0

몇 년 전 MSVC에서 작업 할 때 표준 라이브러리의 멀티 스레드 버전이있었습니다. thread-safe 버전의 stl을 얻을 수 있다면 적어도 컨테이너 당 뮤텍스 mutexA – davka

답변

2

몇 가지 생각.

저장된 개체 당 하나의 뮤텍스가있는 경우 저장된 개체의 생성자에서 해당 뮤텍스를 만들어야합니다. 즉, 캡슐화를 유지하려면 외부 코드가 해당 뮤텍스를 조작하지 않도록해야합니다. 나는 내부적으로 뮤텍스를 처리하는 Setter "SetField1"로 "field1"을 변환 할 것이다.

다음, 당신이 마지막으로 obj1->obtainMutex();

전에 발생하는 pthread_mutex_unlock(&mutexA);를 이동할 수있는 의견에 동의, 난 당신이 모든 obtainMutex 필요하다고 생각하지 않습니다. 하나의 스레드 만 개체를 ​​만들 수 있도록 코드가 작성되므로 개체 생성 중에 하나의 스레드 만 내용을 조작합니다. 그래서 여기에 보여준 작은 코드만을 고려해 보면, 객체 당 뮤텍스가 전혀 필요하지 않은 것처럼 보입니다.

2

코드에서 볼 수있는 한 가지 문제점은 특히 예외가 발생할 때 문제가 발생한다는 것입니다.

obj1->obtainMutex(); //Should I create a mutex for each object so that I could obtain mutex when I am going to update fields for obj1? 
    pthread_mutex_unlock(&mutexA); //release mutex for unordered_map so that other threads could access other object 
    obj1->field1 = 1; 
    performOperation(obj1); 

performOperation이 예외를 throw하면 obj1-> releaseMutex(); 객체가 잠겨져 미래에 언젠가 교착 상태가 될 가능성이 있습니다. 그리고 performOperation에서 사용하는 일부 라이브러리 코드는 예외를 사용하지 않을 수도 있습니다. 또는 실수로 미래에 언젠가 실수로 리턴을 삽입하고 전에 모든 소유 잠금을 해제하는 것을 잊지 마십시오.

pthread_mutex_lock 및 pthread_mutex_unlock 호출도 마찬가지입니다.

잠금/잠금 해제에 RAII를 사용하는 것이 좋습니다.

e.e. 코드는 다음과 같을 수 있습니다.

typedef std::unordered_map<string, classA*>MAP1; 

    MAP1 map1; 

    Lock mapLock(&mutexA); //automatci variable. The destructor of the Lock class 
    //automatically calls pthread_mutex_unlock in its destructor if it "owns" the 
    //mutex 

    if(map1.find(id) == map1.end()) 
    { 
     classA* obj1 = new classA; 
     map1[id] = obj1; 

     Lock objLock(obj); 
     mapLock.release(); //we explicitly release mapLock here 

     obj1->field1 = 1; 
     performOperation(obj1); //takes some time 
    } 

최소한의 RAAI 스레딩 지원에 대한 참조는 Andrei Alexandrescu (see here)의 "Modern C++ design : generic programming and design patterns applied"를 참조하십시오. 다른 리소스도 있습니다 (here)

나는 코드에서 볼 수있는 다른 문제 하나를 설명하려고합니다. 좀 더 정확히 말하자면, obtainMutex와 releaseMutex를 메서드로 가지고 명시 적으로 호출하는 것으로 보이는 문제입니다. 스레드 1이지도를 잠그고 객체를 생성하여 obtainMutex를 호출하고지도를 잠금 해제한다고 가정 해 봅시다. 다른 스레드 (스레드 2라고도 함)가 실행 잠금을 위해 예약 됨 map은 객체의 map1 [id]에 대한 반복자를 가져오고 pObject에 releaseMutex()를 호출합니다 (즉, 코드가 시도하지 않는 버그로 인해 보겠습니다). 먼저 obtainMutex를 호출하십시오.) 이제 스레드 1 일정이 잡히고 어떤 시점에서 호출 releaseMutex(). 그래서 물체는 한 번 잠겼지만 두 번 풀어졌습니다. 내가 말하고자하는 것은 그것이 예외가 발생했을 때 호출이 항상 올바르게 쌍을 이룰 수 있는지, 잠재적으로 잠금을 해제하지 않는 잠재적 인 초기 수익 및 객체 잠금 인터페이스의 잘못된 사용을 확인하는 것이 어려운 작업이라는 것입니다. 또한 스레드 2는지도에서 가져온 pObject를 삭제하고지도에서 지울 수 있습니다. 그러면 스레드 1은 이미 삭제 된 객체가있는 작업으로 이동합니다.

RAII를 사용하면 RAII가 코드를 더 쉽게 이해할 수 있고 (우리 버전을 비교하면 더 짧아집니다) 위의 열거 된 문제 중 일부를 많이 도울 수 있습니다.컨테이너가 될 수 있으므로,

당신이 항목을 추가하고, 따라서 컨테이너를 수정하는

1), 당신은 다른 스레드에서 읽기 액세스를 허용해서는 안 : 대답에 내 의견을 결합

+1

'if' 안에'Lock mapLock (& ​​mutexA);를 움직일 수 있다고 생각합니다. – davka

+0

@davka. 나는 if가 map에 접근하기 때문에 (find를 만든다) 나는 그렇지 않다. map1이 여러 스레드간에 공유되는 객체이고 스레드가 map1.find (id)에있는 동안 다른 스레드가 맵에 삽입하려고하면 map 액세스가 보호되어야한다고 가정합니다. 잠금을 포함하는 범위를 만들고 그 다음에 mapLock이 해제되도록하려면 다음과 같이 범위를 만들 수 있습니다. 또는 if 후에 명시 적으로 해제 할 수 있습니다. 나는 게으른지도 접근을하는 함수가 if ;-) 후에 끝난다고 당연히 생각했다. – ds27680

+0

물론 당신 말이 맞다. 나 자신이 내 대답에 이런 식으로 뭔가 제안 :) 글쎄, 내가 multy - threading에 약간 녹슨했다 ... – davka

0

생각 법적 국가 간 전환 보완 적으로, 다른 스레드가 읽을 때 컨테이너를 수정해서는 안됩니다. 이를 위해서는 read-write lock을 사용해야합니다. 의사 코드는 뭔가 같은 :

set read lock 
search container 
if found 
    release read lock 
    operate on the found object 
else 
    set write lock 
    release read lock 
    add entry 
    release write lock 
endif 

(내가 멀티 스레드 프로그래밍을 한 적이 있기 때문에 약간의 시간이 있었다, 그래서 나는 세부 사항에 녹슨 수 있음)

2) I가 일을 할 때 MSVC 몇 년 전에 (즉, thread-safe) 버전의 표준 라이브러리를 사용했습니다. 이 모든 문제를 해결할 수 있습니다. gcc/Linux에서도 thread-safe std가 존재하는지 확인하기 위해 (아직) 신경 쓰지 않았다.