2017-01-31 21 views
14
#include <iostream> 
#include <sstream> 
#include <thread> 

using namespace std; 

int main() 
{ 
    auto runner = []() { 
     ostringstream oss; 
     for (int i=0; i<100000; ++i) 
      oss << i; 
    }; 

    thread t1(runner), t2(runner); 
    t1.join(); t2.join(); 
} 

g ++ 6.2.1에서 위의 코드를 컴파일 한 다음 valgrind --tool=helgrind ./a.out으로 실행하십시오. Helgrind 불평 것 :두 개의 다른 스트림에있는 연산자 << (ostream &, obj)가 스레드로부터 안전합니까?

==5541== ---------------------------------------------------------------- 
==5541== 
==5541== Possible data race during read of size 1 at 0x51C30B9 by thread #3 
==5541== Locks held: none 
==5541== at 0x4F500CB: widen (locale_facets.h:875) 
==5541== by 0x4F500CB: widen (basic_ios.h:450) 
==5541== by 0x4F500CB: fill (basic_ios.h:374) 
==5541== by 0x4F500CB: std::ostream& std::ostream::_M_insert<long>(long) (ostream.tcc:73) 
==5541== by 0x400CD0: main::{lambda()#1}::operator()() const (43.cpp:12) 
==5541== by 0x4011F7: void std::_Bind_simple<main::{lambda()#1}()>::_M_invoke<>(std::_Index_tuple<>) (functional:1391) 
==5541== by 0x401194: std::_Bind_simple<main::{lambda()#1}()>::operator()() (functional:1380) 
==5541== by 0x401173: std::thread::_State_impl<std::_Bind_simple<main::{lambda()#1}()> >::_M_run() (thread:197) 
==5541== by 0x4EF858E: execute_native_thread_routine (thread.cc:83) 
==5541== by 0x4C31A04: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) 
==5541== by 0x56E7453: start_thread (in /usr/lib/libpthread-2.24.so) 
==5541== by 0x59E57DE: clone (in /usr/lib/libc-2.24.so) 
==5541== 
==5541== This conflicts with a previous write of size 8 by thread #2 
==5541== Locks held: none 
==5541== at 0x4EF3B1F: do_widen (locale_facets.h:1107) 
==5541== by 0x4EF3B1F: std::ctype<char>::_M_widen_init() const (ctype.cc:94) 
==5541== by 0x4F501B7: widen (locale_facets.h:876) 
==5541== by 0x4F501B7: widen (basic_ios.h:450) 
==5541== by 0x4F501B7: fill (basic_ios.h:374) 
==5541== by 0x4F501B7: std::ostream& std::ostream::_M_insert<long>(long) (ostream.tcc:73) 
==5541== by 0x400CD0: main::{lambda()#1}::operator()() const (43.cpp:12) 
==5541== by 0x4011F7: void std::_Bind_simple<main::{lambda()#1}()>::_M_invoke<>(std::_Index_tuple<>) (functional:1391) 
==5541== by 0x401194: std::_Bind_simple<main::{lambda()#1}()>::operator()() (functional:1380) 
==5541== by 0x401173: std::thread::_State_impl<std::_Bind_simple<main::{lambda()#1}()> >::_M_run() (thread:197) 
==5541== by 0x4EF858E: execute_native_thread_routine (thread.cc:83) 
==5541== by 0x4C31A04: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) 
==5541== Address 0x51c30b9 is 89 bytes inside data symbol "_ZN12_GLOBAL__N_17ctype_cE" 

operator <<은 두 개의 서로 다른 ostringstream 객체라고하더라도,이 기능에는 동기화가 나타납니다 없기 때문에 locale_facet.h:widen라는 두 스레드가 데이터 레이스를 일으킨 것으로 보인다. 그래서 이것이 실제로 데이터 경주인지 또는 helgrind의 오 탐지인지 궁금합니다. 나는 완전히 대답하기 전에 질문을 읽어 보지 않았 인정, 그래서 나는이 조회 나 자신에 그것을했다 : 2 개 개의 다른 스트림

+3

이,이 *이 * 스레드를해야한다. –

+2

표준 상태 * 별도로 지정하지 않는 한 여러 스레드에서 스트림 객체 (27.8, 27.9), 스트림 버퍼 객체 (27.6) 또는 C 라이브러리 스트림 (27.9.2) 에 동시 액세스하면 데이터 경합이 발생할 수 있습니다 (1.10). 27.4). [참고 : 데이터 경주 은 정의되지 않은 동작을 발생시킵니다 (1.10). -end note] * 내 생각 엔 스트림이 동기화되지 않은 백엔드에서 일부 전역 상태를 사용하고있는 것 같습니다. 또는 거짓 긍정입니다. 내 직감은 이것이 안전해야한다고 말한다. – NathanOliver

+2

§17.6.5.9/2에 따라 안전해야한다고 생각합니다. * "C++ 표준 라이브러리 함수는 객체가 직접 또는 간접적으로 액세스하지 않는 한 현재 스레드가 아닌 다른 스레드가 액세스 할 수있는 객체 (1.10)에 직접 또는 간접적으로 액세스해서는 안됩니다. 함수의 인수 (this는'this'를 포함합니다.). * 그래서 이것은 부적합한 구현이거나 위양성이라고 말할 수 있습니다. –

답변

0

는 스레드 안전 :

+2

대답을 지원하는 참조 번호를 제공하는 것이 좋습니다 –

1

업데이트입니다.

는 TL; DR

가 경쟁 상태가 발생할 수 여기 가변 멤버 변수가있다. ios에는 로켈 당 정적 변수가 있으며 이러한 정적 변수는 (필요할 때 초기화되는) 참조 표를 지연로드합니다. 동시성 문제를 피하려면 모든 스레드 작업이 이러한 조회 테이블에서만 읽힐 수 있도록 스레드를 결합하기 전에 로캘을 초기화해야합니다.

세부 스트림이 초기화됩니다

로케일에 대한 올바른 CTYPE에로드 포인터를 채 웁니다 (_M_ctype 참조) https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/basic_ios.h#L273

을 오류가 언급된다 https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/locale_facets.h#L875

두 스레드가 동시에 동일한 로캘을 초기화하는 경우 이는 경쟁 조건이 될 수 있습니다.

이 스레드 (여전히 오류를 줄 수 있지만)해야한다 :

표준이 무슨 말씀을 하시든지
// Force ctype to be initialized in the base thread before forking 
ostringstream dump; 
dump << 1; 

auto runner = []() { 
    ostringstream oss; 
    for (int i=0; i<100000; ++i) 
     oss << i; 
}; 

thread t1(runner), t2(runner); 
t1.join(); t2.join(); 
+0

몇 가지 참조를 제공해 주시겠습니까? – lz96

+0

스레드를 포킹하기 전에 많은 일을하는 대형 프로그램에서 이것은 문제가되지 않습니다. – ymmyk

+0

이것은 표준 결함입니까, 아니면 libstdC++의 버그입니까? – lz96