2017-01-31 14 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(); 
} 

Biên dịch mã ở trên trong g ++ 6.2.1, sau đó chạy mã đó bằng valgrind --tool=helgrind ./a.out. Helgrind sẽ phàn nàn:Nhà điều hành << (ostream &, obj) trên hai luồng luồng khác nhau có an toàn không?

==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" 

Dường như cả hai chủ đề được gọi là locale_facet.h:widen khiến cuộc đua dữ liệu kể từ khi có xuất hiện không đồng bộ trong hàm này, mặc dù operator << được kêu gọi hai đối tượng khác nhau ostringstream. Vì vậy, tôi đã tự hỏi liệu đây có thực sự là một cuộc đua dữ liệu hay chỉ là một kết quả dương tính giả của helgrind.

+3

Dù tiêu chuẩn nói, * điều này nên được * threadsafe. –

+2

Trạng thái chuẩn * Truy cập đồng thời vào đối tượng luồng (27.8, 27.9), đối tượng đệm luồng (27.6) hoặc luồng C Library (27.9.2) bằng nhiều chủ đề có thể dẫn đến cuộc đua dữ liệu (1.10) trừ khi có quy định khác (27.4). [Lưu ý: Các cuộc đua dữ liệu dẫn đến hành vi không xác định (1.10). —khi lưu ý] * Vì vậy, tôi đoán là các luồng đang sử dụng một số trạng thái toàn cục ở phần cuối không được đồng bộ hóa. Hoặc nó là dương tính giả. Ruột của tôi nói điều này nên được an toàn. – NathanOliver

+2

Tôi nghĩ rằng điều này nên an toàn theo §17.6.5.9/2: * "Chức năng thư viện chuẩn C++ không được truy cập trực tiếp hoặc gián tiếp các đối tượng (1.10) truy cập bởi các luồng khác với luồng hiện tại trừ khi các đối tượng được truy cập trực tiếp hoặc gián tiếp qua các đối số của hàm, bao gồm 'this'." * Vì vậy, tôi muốn nói đây là một triển khai không phù hợp hoặc một kết quả dương tính giả. –

Trả lời

0

Đối với 2 dòng khác nhau đó là chủ đề an toàn :)

+2

Sẽ rất tốt nếu bạn cung cấp tài liệu tham khảo hỗ trợ câu trả lời –

1

Cập nhật: Tôi thừa nhận tôi đã không đọc đầy đủ các câu hỏi trước khi trả lời, vì vậy tôi lấy nó khi mình để nghiên cứu này.

TL; DR

Có thể thay đổi thành viên biến ở đây có thể gây ra tình trạng chủng tộc. ios có các biến tĩnh cho mỗi miền địa phương, và các biến tĩnh tải lười (khởi tạo khi cần thiết đầu tiên) bảng tra cứu. Nếu bạn muốn tránh các vấn đề tương tranh, chỉ cần chắc chắn để khởi tạo miền địa phương trước khi tham gia các chủ đề sao cho bất kỳ hoạt động luồng nào chỉ được đọc cho các bảng tra cứu này.

Các chi tiết

Khi một dòng được khởi tạo nó populates một con trỏ mà tải trong CType chính xác cho các địa phương (xem _M_ctype): https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/basic_ios.h#L273

Các lỗi được đề cập đến: https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/locale_facets.h#L875

Điều này có thể là điều kiện chủng tộc nếu hai chuỗi đồng thời khởi tạo cùng một ngôn ngữ.

này cần được threadsafe (mặc dù nó vẫn có thể cung cấp cho lỗi):

// 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

Bạn có thể đưa ra một số tham chiếu không? – lz96

+0

Trong một chương trình lớn hơn thực hiện rất nhiều nội dung trước khi tạo chuỗi, đây không phải là vấn đề. – ymmyk

+0

Đây có phải là lỗi tiêu chuẩn hay chỉ là lỗi trong libstdC++? – lz96

Các vấn đề liên quan