2017-01-11 1 views
0

Scott Meyers의 Effective Modern C++에서 Item 16을 읽습니다. 항목의 후반부에서 , 그는 하나의 변수 또는 메모리 위치는 mutex 또는 const 멤버 함수의 원자

std::atomic의 사용이 적절한 동기화를 필요로하지만, 한 번 두 개 이상의 변수 나 같은 조작을 필요로 메모리 위치에 도착 말한다 단위가되면 에 도달해야합니다.

하지만 여전히 하나의 변수 또는 메모리 위치의 경우 적절한 이유를 참조하십시오이 항목에서 다항식 예를하지 않는

class Polynomial { 
public: 
using RootsType = std::vector<double>; 
RootsType roots() const 
{ 
    if (!rootsAreValid) { // if cache not valid 
    .... // **very expensive compuation**, computing roots, 
     // store them in rootVals 
    rootsAreValid = true; 
    } 
return rootVals; 
} 
private: 
mutable std::atomic<bool> rootsAreValid{ false }; 
mutable RootsType rootVals{}; 
}; 

내 질문은 :

스레드 1이 많이 계산되는 중간에 rootAreValidtrue에 할당되고 스레드 2가 또한 함수 roots()을 호출하고 rootAreValid에서 false까지 평가하면 스레드 2가 als가됩니다 o rootVals의 무거운 계산에 대한 단계이므로,이 경우 원자 bool의 적절한 방법은 무엇입니까? 나는 여전히 계산에 대한 입력을 보호하기 위해 std::lock_guard<mutex>이 필요하다고 생각한다.

+0

그것은 분명 충분하지 않다. 견적에 따르면 "단일 변수 나 메모리 위치가 동기화가 필요한 경우 ..."* – juanchopanza

+0

@juanchopanza는 "동기화가 필요한 단일 변수 또는 메모리 위치의 경우"의 예가 아닙니다. – Allanqunzi

+2

아니요, 벡터를 동기화해야합니다. – juanchopanza

답변

2

예를 들어 동기화 할 변수가 두 개 있습니다 (rootValsrootsAreValid). 이 특정 항목은 의 원자 값을 동기화해야하는 경우를 나타냅니다. 예 :

#include <atomic> 

class foo 
{ 
public: 
    void work() 
    { 
     ++times_called; 
     /* multiple threads call this to do work */ 
    } 
private: 
    // Counts the number of times work() was called 
    std::atomic<int> times_called{0}; 
}; 

times_called은이 경우 유일한 변수입니다.

+0

감사합니다. 두 변수가 동기화되고 있음을 알지 못했습니다. – Allanqunzi

0

나는 다음과 같은 코드를 사용하여 불필요한 무거운 계산을하지 않도록하는 것이 좋습니다 :

class Polynomial { 
public: 
using RootsType = std::vector<double>; 

RootsType roots() const 
{ 
    if (!rootsAreValid) { // Acquiring mutex usually is not cheap, so we check the state without locking 
    std::lock_guard<std::mutex> lock_guard(sync); 
    if (!rootsAreValid) // The state could changed because the mutex was not owned at the first check 
    { 
     .... // **very expensive compuation**, computing roots, 
      // store them in rootVals 
    } 
    rootsAreValid = true; 
    } 
    return rootVals; 
} 
private: 
mutable std::mutex sync; 
mutable std::atomic<bool> rootsAreValid{ false }; 
mutable RootsType rootVals{}; 
}; 
+0

'rootsAreValid' (일반 bool) 검사는 보호되지 않습니다 (뮤텍스 외부). 이것은 데이터 경쟁의 고전적인 예이며 유효하지 않습니다. 문제는 그것에 쓴 스레드와 동기화되지 않은 상태에서 반환되는 벡터로 확장됩니다. 결과적으로 정의되지 않은 동작이 발생합니다 – LWimsey

+0

@LWimsey, 데이터 경쟁이 없습니다.'rootsAreValid' 값은'true'에서'false'로 변경할 수 없습니다. 'rootsAreValid'가 false 일 때 두 개 이상의 스레드가 벡터를 계산하려고 시도하지만이 부분이 뮤텍스에 의해 보호되고 추가적인 비교가 이루어질 때 데이터 경쟁이 발생할 수 있습니다. 첫 번째 검사가 신뢰할 수없는 이유는 원자 적 연산이 수십 개의 틱을 소비하기 때문에 성능입니다. –

+0

당신은 데이터 경주가 무엇인지 오해하고 있습니다 : 동일한 메모리 위치에 동시에 접근하고 (적어도) 둘 중 하나가 writer_입니다 ... _이것은'rootsAreValid'와'true'에서 '거짓 '은 부적합하다. 예, 뮤텍스는 단 하나의 스레드 만이 '벡터'에 쓸 수 있다는 것을 보장하지만, 독자 접근이'뮤텍스 (mutex) '외부에 있기 때문에'벡터'가 리더 쓰레드와 정확히 동기화되는 것을 보장하지 않습니다 ... ......... (_ 계속 _) – LWimsey