2016-08-19 5 views
6

मेरा धागा सिंक्रनाइज़ेशन "शैली" हेल्ग्रिंड को फेंकने लग रहा है।हेल्ग्रिंड के साथ झूठी सकारात्मकताओं से कैसे बचें?

#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; 
} 

जहां तक ​​मेरा बता सकते हैं, इसके बाद के संस्करण एक पूरी तरह से अच्छी तरह से गठित कार्यक्रम है: यह एक साधारण प्रोग्राम है कि समस्या reproduces है।

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 एक दौड़ शर्त के रूप में मेरी जबकि पाश उठा किया जा रहा है लेकिन, जब मैं निम्न आदेश का उपयोग कर helgrind चलाने मैं त्रुटियों मिलता है। झूठी सकारात्मकताओं को फेंकने वाले हेल्ग्रिंड से बचने के लिए मुझे इस कार्यक्रम को कैसे बनाया जाना चाहिए?

+1

व्यस्त लूप में पैदा करना सिंक्रनाइज़ेशन का एक खराब रूप है, इसके बजाय 'condition_variable' का उपयोग करने पर विचार करें। –

+0

@ जोनाथन वाकई कैसे? मैंने [std :: condition_variable cppreference] के लिए उदाहरण कोड संकलित करने का प्रयास किया (http://en.cppreference.com/w/cpp/thread/condition_variable) लेकिन परिणामी प्रोग्राम एक संदिग्ध देता है: संबंधित लॉक किसी भी द्वारा नहीं आयोजित किया जाता है थ्रेड 'त्रुटि जब हेल्ग्रिंड के तहत चलाते हैं। – arman

+0

इसे सही तरीके से उपयोग करके :-) –

उत्तर

4

समस्या यह है कि हेल्ग्रिंड जीसीसी के परमाणु बिल्टिन को नहीं समझता है, इसलिए यह नहीं पता कि वे दौड़ मुक्त हैं और कार्यक्रम पर ऑर्डर देने लगते हैं।

हेल्ग्रिंड की मदद के लिए अपने कोड को एनोटेट करने के तरीके हैं, http://valgrind.org/docs/manual/hg-manual.html#hg-manual.effective-use देखें (लेकिन मुझे यकीन नहीं है कि यहां उनका उपयोग कैसे करें, मैंने पहले से ही कोशिश की है कि सब्बी क्या दिखाती है और यह केवल समस्या का हिस्सा हल करती है)।

मैं किसी भी व्यस्त लूप में उपज करने से बचूंगा, यह सिंक्रनाइज़ेशन का एक खराब रूप है। यह बहुत की तरह एक शर्त चर के साथ किया जा सकता है:

#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 :: वादा ' का उपयोग करना पसंद करता हूं, जहां एक थ्रेड एक अन्य थ्रेड को एक बार सिग्नल दे रहा है कि कुछ प्रोसेसिंग किया जाता है। –

+0

आखिरकार हमारे पास ['बाrier'] होगा (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4392.pdf) इसके लिए। –

1

वेलग्रिंड नहीं पता कर सकते while (!isReady) लूप (दुकान और लोड पर निहित memory_order_release और memory_order_consume झंडे के साथ), संकेत मिलता है कि उस वक्तव्य 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= पर एक दमन फ़ाइल मान पर्याप्त होगा।

संबंधित मुद्दे