출시

2017-02-02 6 views
1

측정되는 업데이트 및 일부 자산의 평균을 반환하는 두 가지 기능이 있습니다 말 평균 계산하는 의미를 획득 :출시

void Class::Update(int delta) 
{ 
    m_accumulatedValue += delta; 
    ++ m_count; 
} 

double Class::GetAverage() 
{ 
    return m_accumulatedValue/(double)m_count; 
} 

지금, 그들이 스레드와 다중 스레드 환경에서 실행되도록 변경해야 가정 수영장이있는 모든 스레드는 그 중 하나를 실행하도록 요청할 수 있습니다 - 즉, 그들의 각 하나를 실행 스레드마다 다른 하나가 될 수 있습니다

std::atomic<int> m_accumulatedValue; 
std::atomic<int> m_count; 

// ... 

void Class::Update(int delta) 
{ 
    m_accumulatedValue.fetch_add(delta , std::memory_order_relaxed); 
    m_count.fetch_add(1 , std::memory_order_release); 
} 

double Class::GetAverage() 
{ 
    auto count = m_count.load(std::memory_order_acquire); 
    auto acc = m_accumulatedValue.load(std::memory_order_relaxed); 

    return acc/(double)count; 
} 

나는 취득을 이해하고 메모리를 해제하기 위해 노력하고있어 주문.

Update()에 대한 동일한 개체에는 동시 통화가 없습니다 가정,하지만 Update()GetAverage()에 동일한 개체에 대한 동시 통화 할 수있다.

내가 읽은 것을 들어

GetAverage()에서 m_count의 취득 부하 Update()에 의해 수행 m_accumulatedValue에 어떤 변화가 스레드 호출하여 볼 수 보장하는 동시에 그 전에 m_accumulatedValue의 부하의 재정렬 및 ​​금지 GetAverage()m_count로 변경하면 상점에서 수행 한 m_coutUpdate()으로 출시 주문이 있습니다.

내가 방금 말한 것이 맞습니까?

GetAverage() (Update()에 대한 호출의 비 동시성 보장과 함께) 항상 올바른 대답을 반환합니까? 아니면 계산 된 평균값을 다른 값보다 "더 업데이트 된"값으로 반환하는 방법이있을 수 있습니까?

m_accumulatedValue은 원자 일 필요가 있습니까?

+1

'memory_order_relaxed'는 다른 스레드가 새로운 값을 보지 못하게하지 않으며, 단지 그것을 요구하지 않습니다.따라서 여전히 경쟁 조건이 존재할 수 있습니다. –

+2

메모리 순서 의미를 제외하고이 코드는 손상되었습니다. 하나의 가능한 실행 순서는 한 스레드가'm_accumulatedValue'에 추가 한 다음 다른 스레드가'm_accumulatedValue' ** 및 **'m_count'를 읽으면 첫 번째 스레드는'm_count'를 업데이트합니다. 두 번째 스레드가 계산 한 평균은 잘못 될 것입니다. –

+0

번호. 아마도. 상호 배제를 사용하지 않는 이유는 무엇입니까? – jotik

답변

0

획득/릴리스 시맨틱 작동 방식에 대한 설명이 정확합니다. 그들은 는-전에 일어나는 저장/방출 전 부하/획득 후에 메모리 동작 사이 관계 ... 이것은 런타임 관계에 기초하는 스레드 간을 생성하는데 사용 는 경우가 정의되고 원자로드/획득은 상점/릴리스에 의해 설정된 값을 보게됩니다.

코드의 첫 번째 문제점은이 런타임 요구 사항을 충족하지 못한다는 것입니다. m_count의 값은 확인되지 않으므로 주문 보증이 적용되지 않습니다. 따라서 모든 작업에 memory_order_relaxed을 사용할 수도 있습니다.

하지만 그만으로는 문제가 해결되지 않습니다. m_accumulatedValue을 읽을 때 그 값은 Update() (m_accumulatedValue)이 다시 호출되어 다시 변경되었을 수 있습니다. 또한 주석 섹션에서 지적한 것처럼 원자 작업간에 원 자성이 없으므로 Update()이 완료되기 전에 GetAverage()이 호출되어 잘못된 값을 반환 할 수 있습니다.

Update()GetAverage() 사이에 엄격한 주문이 필요하며이를 수행하는 가장 좋은 방법은 std::mutex입니다. 원자 변수는 정규 정수가 될 수 있습니다 (다른 곳에서는 사용되지 않는 경우).