2013-07-16 6 views
12

वेलग्रिंड लीक ब्लॉक रिपोर्ट कर रहा है, जाहिरा तौर पर धागा प्रति एक, निम्नलिखित कोड में:थ्रेड_लोकल का उपयोग करते समय जीसीसी 4.8.1 में मेमोरी लीक?

#include <iostream> 
#include <thread> 
#include <mutex> 
#include <list> 
#include <chrono> 

std::mutex cout_mutex; 

struct Foo 
{ 
    Foo() 
    { 
     std::lock_guard<std::mutex> lock(cout_mutex); 
     std::cout << __PRETTY_FUNCTION__ << '\n'; 
    } 

    ~Foo() 
    { 
     std::lock_guard<std::mutex> lock(cout_mutex); 
     std::cout << __PRETTY_FUNCTION__ << '\n'; 
    } 

    void 
    hello_world() 
    { 
     std::lock_guard<std::mutex> lock(cout_mutex); 
     std::cout << __PRETTY_FUNCTION__ << '\n'; 
    } 
}; 

void 
hello_world_thread() 
{ 
    thread_local Foo foo; 

    // must access, or the thread local variable may not be instantiated 
    foo.hello_world(); 

    // keep the thread around momentarily 
    std::this_thread::sleep_for(std::chrono::milliseconds(100)); 
} 

int main() 
{ 
    for (int i = 0; i < 100; ++i) 
    { 
     std::list<std::thread> threads; 

     for (int j = 0; j < 10; ++j) 
     { 
      std::thread thread(hello_world_thread); 
      threads.push_back(std::move(thread)); 
     } 

     while (! threads.empty()) 
     { 
      threads.front().join(); 
      threads.pop_front(); 
     } 
    } 
} 

संकलक संस्करण:

$ g++ --version 
g++ (GCC) 4.8.1 
Copyright (C) 2013 Free Software Foundation, Inc. 
This is free software; see the source for copying conditions. There is NO 
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 

जीसीसी का निर्माण विकल्प:

--enable-shared 
--enable-threads=posix 
--enable-__cxa_atexit 
--enable-clocale=gnu 
--enable-cxx-flags='-fno-omit-frame-pointer -g3' 
--enable-languages=c,c++ 
--enable-libstdcxx-time=rt 
--enable-checking=release 
--enable-build-with-cxx 
--disable-werror 
--disable-multilib 
--disable-bootstrap 
--with-system-zlib 

कार्यक्रम संकलन विकल्प:

g++ -std=gnu++11 -Og -g3 -Wall -Wextra -fno-omit-frame-pointer thread_local.cc 

valgrind संस्करण:

$ valgrind --version 
valgrind-3.8.1 

वेलग्रिंड विकल्प: valgrind उत्पादन का

valgrind --leak-check=full --verbose ./a.out > /dev/null 

पूंछ अंत:

==1786== HEAP SUMMARY: 
==1786==  in use at exit: 24,000 bytes in 1,000 blocks 
==1786== total heap usage: 3,604 allocs, 2,604 frees, 287,616 bytes allocated 
==1786== 
==1786== Searching for pointers to 1,000 not-freed blocks 
==1786== Checked 215,720 bytes 
==1786== 
==1786== 24,000 bytes in 1,000 blocks are definitely lost in loss record 1 of 1 
==1786== at 0x4C29969: operator new(unsigned long, std::nothrow_t const&) (vg_replace_malloc.c:329) 
==1786== by 0x4E8E53E: __cxa_thread_atexit (atexit_thread.cc:119) 
==1786== by 0x401036: hello_world_thread() (thread_local.cc:34) 
==1786== by 0x401416: std::thread::_Impl<std::_Bind_simple<void (*())()> >::_M_run() (functional:1732) 
==1786== by 0x4EE4830: execute_native_thread_routine (thread.cc:84) 
==1786== by 0x5A10E99: start_thread (pthread_create.c:308) 
==1786== by 0x573DCCC: clone (clone.S:112) 
==1786== 
==1786== LEAK SUMMARY: 
==1786== definitely lost: 24,000 bytes in 1,000 blocks 
==1786== indirectly lost: 0 bytes in 0 blocks 
==1786==  possibly lost: 0 bytes in 0 blocks 
==1786== still reachable: 0 bytes in 0 blocks 
==1786==   suppressed: 0 bytes in 0 blocks 
==1786== 
==1786== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2) 
--1786-- 
--1786-- used_suppression:  2 dl-hack3-cond-1 
==1786== 
==1786== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2) 

निर्माणकर्ता और विनाशकर्ता प्रत्येक थ्रेड के लिए एक बार चलाने गया:

$ ./a.out | grep 'Foo::Foo' | wc -l 
1000 

$ ./a.out | grep hello_world | wc -l 
1000 

$ ./a.out | grep 'Foo::~Foo' | wc -l 
1000 

नोट्स:

  • आपके द्वारा बनाए गए थ्रेड की संख्या बदलते हैं, तो लीक ब्लॉक की संख्या थ्रेड की संख्या से मेल खाता है।
  • कोड इस तरह से संरचित किया गया है कि परमिट संसाधन पुन: उपयोग (यानी लीक ब्लॉक) अगर जीसीसी लागू किया गया हो। एक कार्यक्रम चलाने के बारे में 10 सेकंड का समय लगता है, sleep_for() कॉल के कारण thread_local Foo foo;
  • :
  • valgrind स्टैकट्रेस से, thread_local.cc:34 रेखा है।

कोई विचार अगर यह स्मृति रिसाव जीसीसी में है, तो मेरे कॉन्फ़िगरेशन विकल्पों का परिणाम है, या मेरे प्रोग्राम में कुछ बग है?

+3

यह एक पूरी तरह से तैयार प्रश्न का एक चमकदार उदाहरण है, अच्छी तरह से किया गया। – GManNickG

+0

आप 'स्थिर 'का उपयोग क्यों करते हैं? – stefan

+0

फिक्स्ड। मैं valgrind फिर से भाग गया, लेकिन दुर्भाग्य से रिसाव अभी भी मौजूद है। – user2224952

उत्तर

2

ऐसा लगता है कि रिसाव गतिशील प्रारंभ से आता है।

thread_local int num=4; //static initialization 

पिछले उदाहरण लीक नहीं करता:

यहाँ एक int साथ एक उदाहरण है। मैंने इसे 2 धागे और किसी भी रिसाव के साथ कोशिश की।

लेकिन अब:

int func() 
{ 
    return 4; 
} 
thread_local int num2=func(); //dynamic initialization 

यह एक रिसाव!

thread_local Foo *foo = new Foo; //dynamic initialization 

कोई धागा निष्पादन के अंत में भूल करने के लिए:

delete foo; 

लेकिन 2 धागे यह total heap uage: 8 allocs, 6 frees, 428 bytes allocated देता है ...

मैं आप की तरह एक तरीके का उपयोग करने के लिए सुझाव है कि के साथ आखिरी उदाहरण एक समस्या के रूप में: क्या होगा यदि आपके डिलीट से पहले त्रुटि के साथ थ्रेड निकास हो? लीक फिर से ...

ऐसा लगता है कि कोई अच्छा समाधान नहीं है। शायद हमें इसके बारे में g++ डेवलपर्स को रिपोर्ट करनी चाहिए?

+0

मैंने इस समस्या के लिए एक बग रिपोर्ट बनाई: [link] (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57914) – user2224952

0

thread_local को दूर करने और निम्नलिखित कोड hello_world_thread भीतर

void 
    hello_world_thread() 
    { 
     Foo foo; 

     // must access, or the thread local variable may not be instantiated 
     foo.hello_world(); 

     // keep the thread around momentarily 
     std::this_thread::sleep_for(std::chrono::milliseconds(100)); 
    } 

foo हर थ्रेड के लिए स्थानीय ढेर में होना चाहिए उपयोग करके देखें। इसलिए हर धागा फू की अपनी प्रति बनाए रखेगा। इसे thread_local के रूप में स्पष्ट रूप से चिह्नित करने की आवश्यकता नहीं है। एक थ्रेड_लोकल का संदर्भ किसी संदर्भ में किया जाना चाहिए जब आपके पास स्थैतिक या नेमस्पेस लेवल वैरिएबल जैसा कुछ हो लेकिन आप प्रत्येक चर को प्रत्येक थ्रेड के लिए अपनी प्रतिलिपि बनाए रखना चाहते हैं।

सादर काजल

+0

वास्तविक उपयोग-मामला कुछ और हो सकता है: 'Foo & foo() { thread_local Foo foo; वापसी foo; } शून्य hello_world_thread() { अगर (some_condition) { foo() hello_world()।; } } ' थ्रेड द्वारा संदर्भित होने पर थ्रेड स्थानीय ऑब्जेक्ट केवल तत्काल (और नष्ट) किया जाएगा। ऐसा प्रतीत होता है कि मैंने गलत कोड में अपना कोड उदाहरण कम किया और जटिलता जोड़ा जहां शायद मुझे नहीं होना चाहिए (यानी थ्रेड निर्माण व्यवहार)। – user2224952

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