2016-08-19 2 views
6

내 스레드 동기화 "스타일"이 helgrind를 끄는 것처럼 보입니다. 다음은 문제를 재현하는 간단한 프로그램입니다.Helgrind에서 가양 성을 피하는 방법은 무엇입니까?

#include <thread> 
#include <atomic> 
#include <iostream> 

int main() 
{ 
    std::atomic<bool> isReady(false); 

    int i = 1; 

    std::thread t([&isReady, &i]() 
    { 
     i = 2; 
     isReady = true; 
    }); 

    while (!isReady) 
     std::this_thread::yield(); 

    i = 3; 

    t.join(); 

    std::cout << i; 

    return 0; 
} 

위의 내용은 완벽하게 올바른 프로그램입니다.

valgrind --tool=helgrind ./a.out 

이의 출력은 다음과 같습니다 :

==6247== Helgrind, a thread error detector 
==6247== Copyright (C) 2007-2015, and GNU GPL'd, by OpenWorks LLP et al. 
==6247== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info 
==6247== Command: ./a.out 
==6247== 
==6247== ---Thread-Announcement------------------------------------------ 
==6247== 
==6247== Thread #1 is the program's root thread 
==6247== 
==6247== ---Thread-Announcement------------------------------------------ 
==6247== 
==6247== Thread #2 was created 
==6247== at 0x56FBB1E: clone (clone.S:74) 
==6247== by 0x4E46189: create_thread (createthread.c:102) 
==6247== by 0x4E47EC3: [email protected]@GLIBC_2.2.5 (pthread_create.c:679) 
==6247== by 0x4C34BB7: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) 
==6247== by 0x5115DC2: std::thread::_M_start_thread(std::shared_ptr<std::thread::_Impl_base>, void (*)()) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21) 
==6247== by 0x4010EF: std::thread::thread<main::{lambda()#1}>(main::{lambda()#1}&&) (in /home/arman/a.out) 
==6247== by 0x400F93: main (in /home/arman/a.out) 
==6247== 
==6247== ---------------------------------------------------------------- 
==6247== 
==6247== Possible data race during read of size 1 at 0xFFF00035B by thread #1 
==6247== Locks held: none 
==6247== at 0x4022C3: std::atomic<bool>::operator bool() const (in /home/arman/a.out) 
==6247== by 0x400F9F: main (in /home/arman/a.out) 
==6247== 
==6247== This conflicts with a previous write of size 1 by thread #2 
==6247== Locks held: none 
==6247== at 0x40233D: std::__atomic_base<bool>::operator=(bool) (in /home/arman/a.out) 
==6247== by 0x40228E: std::atomic<bool>::operator=(bool) (in /home/arman/a.out) 
==6247== by 0x400F4A: main::{lambda()#1}::operator()() const (in /home/arman/a.out) 
==6247== by 0x40204D: void std::_Bind_simple<main::{lambda()#1}()>::_M_invoke<>(std::_Index_tuple<>) (in /home/arman/a.out) 
==6247== by 0x401FA3: std::_Bind_simple<main::{lambda()#1}()>::operator()() (in /home/arman/a.out) 
==6247== by 0x401F33: std::thread::_Impl<std::_Bind_simple<main::{lambda()#1}()> >::_M_run() (in /home/arman/a.out) 
==6247== by 0x5115C7F: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21) 
==6247== by 0x4C34DB6: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) 
==6247== Address 0xfff00035b is on thread #1's stack 
==6247== in frame #1, created by main (???:) 
==6247== 
==6247== ---------------------------------------------------------------- 
==6247== 
==6247== Possible data race during write of size 4 at 0xFFF00035C by thread #1 
==6247== Locks held: none 
==6247== at 0x400FAE: main (in /home/arman/a.out) 
==6247== 
==6247== This conflicts with a previous write of size 4 by thread #2 
==6247== Locks held: none 
==6247== at 0x400F35: main::{lambda()#1}::operator()() const (in /home/arman/a.out) 
==6247== by 0x40204D: void std::_Bind_simple<main::{lambda()#1}()>::_M_invoke<>(std::_Index_tuple<>) (in /home/arman/a.out) 
==6247== by 0x401FA3: std::_Bind_simple<main::{lambda()#1}()>::operator()() (in /home/arman/a.out) 
==6247== by 0x401F33: std::thread::_Impl<std::_Bind_simple<main::{lambda()#1}()> >::_M_run() (in /home/arman/a.out) 
==6247== by 0x5115C7F: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21) 
==6247== by 0x4C34DB6: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) 
==6247== by 0x4E476F9: start_thread (pthread_create.c:333) 
==6247== by 0x56FBB5C: clone (clone.S:109) 
==6247== Address 0xfff00035c is on thread #1's stack 
==6247== in frame #0, created by main (???:) 
==6247== 
3==6247== 
==6247== For counts of detected and suppressed errors, rerun with: -v 
==6247== Use --history-level=approx or =none to gain increased speed, at 
==6247== the cost of reduced accuracy of conflicting-access information 
==6247== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0) 

Helgrind가 경쟁 조건으로 내 while 루프를 따기 것 같다 나는 다음과 같은 명령을 사용하여 helgrind 실행할 ​​때, 나는 오류를 얻을. 가양 성이 잘못되었다는 것을 피하기 위해이 프로그램을 어떻게 구성해야합니까?

+1

바쁜 루프에서 수익을내는 것은 어쨌든 가난한 형태의 동기화입니다. 대신 'condition_variable'을 사용해보십시오. –

+0

@ 조나단 어떻게? 나는 [std :: condition_variable in cppreference] (http://en.cppreference.com/w/cpp/thread/condition_variable)에 대한 예제 코드를 컴파일 해 보았지만, 결과 프로그램은 '모호한 : 연관된 잠금은 스레드 '오류가 helgrind에서 실행될 때 발생합니다. – arman

+0

제대로 사용하여 :-) –

답변

4

문제는 Helgrind가 GCC의 기본 내장 함수를 이해하지 못하기 때문에 인력 부족이 아니라 프로그램에서 순서를 지정한다는 것을 인식하지 못합니다.

Helgrind를 돕기 위해 코드에 주석을다는 방법이 있습니다. http://valgrind.org/docs/manual/hg-manual.html#hg-manual.effective-use을 참조하십시오 (그러나 여기에서 어떻게 사용하는지 모르겠지만, 이미 sbabbi가 보여 주었던 것을 시도해 보았고 문제의 일부만 해결했습니다).

나는 바쁜 루프에서 항복하는 것을 피할 수 있습니다. 어쨌든 그것은 불량한 형태의 동기화입니다. 그것은과 같이 조건 변수와 함께 할 수있다 :

#include <thread> 
#include <atomic> 
#include <iostream> 
#include <condition_variable> 

int main() 
{ 
    bool isReady(false); 
    std::mutex mx; 
    std::condition_variable cv; 

    int i = 1; 

    std::thread t([&isReady, &i, &mx, &cv]() 
    { 
     i = 2; 
     std::unique_lock<std::mutex> lock(mx); 
     isReady = true; 
     cv.notify_one(); 
    }); 

    { 
     std::unique_lock<std::mutex> lock(mx); 
     cv.wait(lock, [&] { return isReady; }); 
    } 

    i = 3; 

    t.join(); 

    std::cout << i; 

    return 0; 
} 
+1

덧붙여 말하자면, 실제로 상황에 따라'std :: promise '을 사용하고 싶습니다. 스레드는 어떤 처리가 완료된 다른 스레드에게 일회성 신호를 보내고 있습니다. –

+0

결국 우리는이를 위해 ['barrier'] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4392.pdf)를 할 것입니다. –

1

Valgrind의은 (상점 및 부하에 대한 암시 memory_order_releasememory_order_consume 플래그와 함께) while (!isReady) 루프, 문 i = 2 주문 의존성이 있음을 의미 있음을 알 수 없다i = 3

명시 적으로 Valgrind의 ANNOTATE_HAPPENS_BEFORE를 사용하여이 불변을 명시해야

ANNOTATE_HAPPENS_AFTER 매크로 : 여기

#include <valgrind/drd.h> 

#include <thread> 
#include <atomic> 
#include <iostream> 

int main() 
{ 
    std::atomic<bool> isReady(false); 

    int i = 1; 

    std::thread t([&isReady, &i]() 
    { 
     i = 2; 
     ANNOTATE_HAPPENS_BEFORE(&isReady); 
     isReady = true; 
    }); 

    while (!isReady) 
     std::this_thread::yield(); 

    ANNOTATE_HAPPENS_AFTER(&isReady); 
    i = 3; 

    t.join(); 

    std::cout << i; 

    return 0; 
} 

우리가 ANNOTATE_HAPPENS_BEFORE에서 선이 항상 ANNOTATE_HAPPENS_AFTER의 라인 전에 발생한다는 말은, 우리는 그 때문에 검사로 알 프로그램 논리이지만 valgrind는 그것을 증명할 수 없습니다.

이 프로그램은 생산 :

valgrind --tool=helgrind ./a.out 
==714== Helgrind, a thread error detector 
==714== Copyright (C) 2007-2015, and GNU GPL'd, by OpenWorks LLP et al. 
==714== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info 
==714== Command: ./val 
==714== 
==714== ---Thread-Announcement------------------------------------------ 
==714== 
==714== Thread #1 is the program's root thread 
==714== 
==714== ---Thread-Announcement------------------------------------------ 
==714== 
==714== Thread #2 was created 
==714== at 0x59E169E: clone (in /usr/lib/libc-2.23.so) 
==714== by 0x4E421D9: create_thread (in /usr/lib/libpthread-2.23.so) 
==714== by 0x4E43C42: [email protected]@GLIBC_2.2.5 (in /usr/lib/libpthread-2.23.so) 
==714== by 0x4C316F3: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) 
==714== by 0x4C327D7: [email protected]* (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) 
==714== by 0x5113DB4: __gthread_create (gthr-default.h:662) 
==714== by 0x5113DB4: std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) (thread.cc:163) 
==714== by 0x40109C: std::thread::thread<main::{lambda()#1}>(main::{lambda()#1}&&) (in /home/ennio/val) 
==714== by 0x400F55: main (in /home/ennio/val) 
==714== 
==714== ---------------------------------------------------------------- 
==714== 
==714== Possible data race during read of size 1 at 0xFFF00061F by thread #1 
==714== Locks held: none 
==714== at 0x401585: std::atomic<bool>::operator bool() const (in /home/ennio/val) 
==714== by 0x400F61: main (in /home/ennio/val) 
==714== 
==714== This conflicts with a previous write of size 1 by thread #2 
==714== Locks held: none 
==714== at 0x4015D5: std::__atomic_base<bool>::operator=(bool) (in /home/ennio/val) 
==714== by 0x401550: std::atomic<bool>::operator=(bool) (in /home/ennio/val) 
==714== by 0x400F1B: main::{lambda()#1}::operator()() const (in /home/ennio/val) 
==714== by 0x40146F: void std::_Bind_simple<main::{lambda()#1}()>::_M_invoke<>(std::_Index_tuple<>) (in /home/ennio/val) 
==714== by 0x40140C: std::_Bind_simple<main::{lambda()#1}()>::operator()() (in /home/ennio/val) 
==714== by 0x4013EB: std::thread::_State_impl<std::_Bind_simple<main::{lambda()#1}()> >::_M_run() (in /home/ennio/val) 
==714== by 0x5113A9E: execute_native_thread_routine (thread.cc:83) 
==714== by 0x4C318E7: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) 
==714== Address 0xfff00061f is on thread #1's stack 
==714== in frame #1, created by main (???:) 
==714== 
3==714== 
==714== For counts of detected and suppressed errors, rerun with: -v 
==714== Use --history-level=approx or =none to gain increased speed, at 
==714== the cost of reduced accuracy of conflicting-access information 
==714== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) 

isReady 자체에 오류를 제거하기 위해, 나는 충분할 것이다 __atomic_base<bool>::operator=에 억제 파일을 가정합니다.

관련 문제