2013-03-04 18 views
6

मैं सी में पर्थ्रेड के साथ शुरू कर रहा हूं और मैं अपने कोड को "बग-फ्री" के रूप में लिखने का पागल हूं, जैसा कि संभवतः मैं कर सकता हूं।सी pthreads + valgrind = स्मृति रिसाव: क्यों?

अतिरिक्त सावधान, valgrind कह रहा है कि मैं स्मृति लीक कर रहा हूँ, भले ही मौसम होने की कोशिश कर के बावजूद:

  1. मैं joinable धागे कि मैं पूरा होने पर शामिल होने का सृजन (कोड स्निपेट 1)
  2. मैं joinable बनाने धागे कि मैं निर्माण के बाद अलग (कोड स्निपेट 2)
  3. मैं अलग धागे बनाने (कोड स्निपेट 3)

मैं जानता हूँ कि यह पहले से ही चर्चा की गई है (देखना, this और भी this), लेकिन मैं के रूप में अभी भी उत्सुक हूँ:

  1. क्यों कुछ रन मैं कोई त्रुटि के साथ अंत पर?
  2. पृथक धागे से निपटने के दौरान कुल mallocs() की यादृच्छिक संख्या क्यों प्रतीत होती है? < < उत्तर द्वारा प्रदान किया गया उत्तर, कोड स्निपेट मुख्य()
  3. में अतिरिक्त विलंब के साथ "निश्चित" "अलग-अलग धागे से निपटने के दौरान भी" स्मृति-रिसाव "क्यों जारी रहता है? < < 2.

मैं पिछले जवाब और valgrind का पता लगाने से समझने के रूप में के रूप में ही, pthread_create(), मूल कारण है धागे द्वारा इस्तेमाल किया ढेर इस प्रकार कुछ लापता विस्तार के रूप में आवश्यक और कई बार यह पुन: उपयोग, मुक्त करता है। लेकिन कम स्पष्टता यह है कि यह निष्पादन चलाने पर निर्भर करता है और अलग-अलग धागे बनाते समय यह क्यों होता है। जैसा कि मैंने कुछ उत्तरों, टिप्पणियों और मनुष्य से भी देखा है, एक अलग धागे से संसाधन थ्रेड पूरा होने पर मुक्त हो जाएंगे। मैंने इस के आसपास काम करने के लिए विभिन्न बदलावों की कोशिश की है (मुख्य धागे के अंत से पहले, प्रत्येक धागे के अंत से पहले नींद का समय जोड़ा, ढेर के आकार में वृद्धि, और अधिक "काम" जोड़ें ...) लेकिन यह नहीं बदला बहुत से परिणाम परिणाम। साथ ही, पृथक धागे से निपटने के दौरान समग्र "mallocs()" की एक यादृच्छिक संख्या क्यों है, क्या वाल्ग्रिंड कुछ अलग धागे का ट्रैक खो देता है? यह स्टैक आकार पर निर्भर नहीं लगता है।

प्रदान किया गया कोड प्रबंधक/श्रमिक मॉडल का एक नकली उदाहरण है जिसके लिए थ्रेड प्रबंधन में एक जुड़ने योग्य/शामिल() दृष्टिकोण अधिक उपयुक्त imho लगता है।

किसी भी ज्ञान के लिए धन्यवाद जो आप प्रदान करने में सक्षम हो सकते हैं! मुझे यह भी उम्मीद है कि कोड के इन (अधिक टिप्पणी वाले) स्निपेट उपयोगी होंगे जो किसी भी व्यक्ति के साथ शुरू करने की इच्छा रखते हैं।

- Swappy

पुनश्च Sys जानकारी: डेबियन 64 बिट के आर्क पर जीसीसी

कोड स्निपेट 1 (joinable धागे में शामिल हो गए):

/* Running this multiple times with valgrind, I sometimes end with : 
    - no errors (proper malloc/free balance) 
    - 4 extra malloc vs free (most frequently) 
    The number of mallocs() is more conservative and depends on the number of threads. 
*/ 

#include <stdlib.h>    /* EXIT_FAILURE, EXIT_SUCCESS macros & the likes */ 
#include <stdio.h>    /* printf() & the likes */ 
#include <pthread.h>   /* test subject */ 

#define MAX_THREADS 100   /* Number of threads */ 
pthread_attr_t tattr;   /* Thread attribute */ 
pthread_t workers[MAX_THREADS]; /* All the threads spawned by the main() thread */ 

/* A mock container structure to pass arguments around */ 
struct args_for_job_t { 
    int tid; 
    int status; 
}; 

/* The job each worker will perform upon creation */ 
void *job(void *arg) 
{ 
    /* Cast arguments in a proper container */ 
    struct args_for_job_t *container; 
    container = (struct args_for_job_t *)arg; 

    /* A mock job */ 
    printf("[TID - %d]\n", container->tid); 

    /* Properly exit with status code tid */ 
    pthread_exit((void *)(&container->status)); 
} 

int main() 
{ 
    int return_code;       /* Will hold return codes */ 
    void *return_status;      /* Will hold return status */ 
    int tid;         /* Thread id */ 
    struct args_for_job_t args[MAX_THREADS]; /* For thread safeness */ 

    /* Initialize and set thread joinable attribute */ 
    pthread_attr_init(&tattr); 
    pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE); 

    /* Spawn detached threads */ 
    for (tid = 0; tid < MAX_THREADS; tid++) 
    { 
     args[tid].tid = tid; 
     args[tid].status = tid; 
     return_code = pthread_create(&workers[tid], &tattr, job, (void *)(&args[tid])); 
     if (return_code != 0) { printf("[ERROR] Thread creation failed\n"); return EXIT_FAILURE; } 
    } 

    /* Free thread attribute */ 
    pthread_attr_destroy(&tattr); 

    /* Properly join() all workers before completion */ 
    for(tid = 0; tid < MAX_THREADS; tid++) 
    { 
     return_code = pthread_join(workers[tid], &return_status); 
     if (return_code != 0) 
     { 
      printf("[ERROR] Return code from pthread_join() is %d\n", return_code); 
      return EXIT_FAILURE; 
     } 
     printf("Thread %d joined with return status %d\n", tid, *(int *)return_status); 
    } 

    return EXIT_SUCCESS; 
} 

कोड स्निपेट 2 (अलग धागे निर्माण के बाद):

/* Running this multiple times with valgrind, I sometimes end with : 
    - no errors (proper malloc/free balance) 
    - 1 extra malloc vs free (most frequently) 
    Most surprisingly, it seems there is a random amount of overall mallocs 
*/ 

#include <stdlib.h>    /* EXIT_FAILURE, EXIT_SUCCESS macros & the likes */ 
#include <stdio.h>    /* printf() & the likes */ 
#include <pthread.h>   /* test subject */ 
#include <unistd.h>   

#define MAX_THREADS 100   /* Number of threads */ 
pthread_attr_t tattr;   /* Thread attribute */ 
pthread_t workers[MAX_THREADS]; /* All the threads spawned by the main() thread */ 

/* A mock container structure to pass arguments around */ 
struct args_for_job_t { 
    int tid; 
}; 

/* The job each worker will perform upon creation */ 
void *job(void *arg) 
{ 
    /* Cast arguments in a proper container */ 
    struct args_for_job_t *container; 
    container = (struct args_for_job_t *)arg; 

    /* A mock job */ 
    printf("[TID - %d]\n", container->tid); 

    /* For the sake of returning something, not necessary */ 
    return NULL; 
} 

int main() 
{ 
    int return_code;       /* Will hold return codes */ 
    int tid;         /* Thread id */ 
    struct args_for_job_t args[MAX_THREADS]; /* For thread safeness */ 

    /* Initialize and set thread joinable attribute */ 
    pthread_attr_init(&tattr); 
    pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE); 

    /* Spawn detached threads */ 
    for (tid = 0; tid < MAX_THREADS; tid++) 
    { 
     args[tid].tid = tid; 
     return_code = pthread_create(&workers[tid], &tattr, job, (void *)(&args[tid])); 
     if (return_code != 0) { printf("[ERROR] Thread creation failed\n"); return EXIT_FAILURE; } 
     /* Detach worker after creation */ 
     pthread_detach(workers[tid]); 
    } 

    /* Free thread attribute */ 
    pthread_attr_destroy(&tattr); 

    /* Delay main() completion until all detached threads finish their jobs. */ 
    usleep(100000); 
    return EXIT_SUCCESS; 
} 

कोड स्निपेट 3 (निर्माण पर धागे अलग):

/* Running this multiple times with valgrind, I sometimes end with : 
    - no errors (proper malloc/free balance) 
    - 1 extra malloc vs free (most frequently) 
    Most surprisingly, it seems there is a random amount of overall mallocs 
*/ 

#include <stdlib.h>    /* EXIT_FAILURE, EXIT_SUCCESS macros & the likes */ 
#include <stdio.h>    /* printf() & the likes */ 
#include <pthread.h>   /* test subject */ 

#define MAX_THREADS 100   /* Number of threads */ 
pthread_attr_t tattr;   /* Thread attribute */ 
pthread_t workers[MAX_THREADS]; /* All the threads spawned by the main() thread */ 

/* A mock container structure to pass arguments around */ 
struct args_for_job_t { 
    int tid; 
}; 

/* The job each worker will perform upon creation */ 
void *job(void *arg) 
{ 
    /* Cast arguments in a proper container */ 
    struct args_for_job_t *container; 
    container = (struct args_for_job_t *)arg; 

    /* A mock job */ 
    printf("[TID - %d]\n", container->tid); 

    /* For the sake of returning something, not necessary */ 
    return NULL; 
} 

int main() 
{ 
    int return_code;       /* Will hold return codes */ 
    int tid;         /* Thread id */ 
    struct args_for_job_t args[MAX_THREADS]; /* For thread safeness */ 

    /* Initialize and set thread detached attribute */ 
    pthread_attr_init(&tattr); 
    pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); 

    /* Spawn detached threads */ 
    for (tid = 0; tid < MAX_THREADS; tid++) 
    { 
     args[tid].tid = tid; 
     return_code = pthread_create(&workers[tid], &tattr, job, (void *)(&args[tid])); 
     if (return_code != 0) { printf("[ERROR] Thread creation failed\n"); return EXIT_FAILURE; } 
    } 

    /* Free thread attribute */ 
    pthread_attr_destroy(&tattr); 

    /* Delay main() completion until all detached threads finish their jobs. */ 
    usleep(100000); 
    return EXIT_SUCCESS; 
} 
कोड स्निपेट 1 (शामिल हो गए धागे & मेम-रिसाव)

==27802== 
==27802== HEAP SUMMARY: 
==27802==  in use at exit: 1,558 bytes in 4 blocks 
==27802== total heap usage: 105 allocs, 101 frees, 28,814 bytes allocated 
==27802== 
==27802== Searching for pointers to 4 not-freed blocks 
==27802== Checked 104,360 bytes 
==27802== 
==27802== 36 bytes in 1 blocks are still reachable in loss record 1 of 4 
==27802== at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==27802== by 0x400894D: _dl_map_object (dl-load.c:162) 
==27802== by 0x401384A: dl_open_worker (dl-open.c:225) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x4013319: _dl_open (dl-open.c:639) 
==27802== by 0x517F601: do_dlopen (dl-libc.c:89) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x517F6C3: __libc_dlopen_mode (dl-libc.c:48) 
==27802== by 0x4E423BB: pthread_cancel_init (unwind-forcedunwind.c:53) 
==27802== by 0x4E4257B: _Unwind_ForcedUnwind (unwind-forcedunwind.c:130) 
==27802== by 0x4E4069F: __pthread_unwind (unwind.c:130) 
==27802== by 0x4E3AFF4: pthread_exit (pthreadP.h:265) 
==27802== 
==27802== 36 bytes in 1 blocks are still reachable in loss record 2 of 4 
==27802== at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==27802== by 0x400B7EC: _dl_new_object (dl-object.c:161) 
==27802== by 0x4006805: _dl_map_object_from_fd (dl-load.c:1051) 
==27802== by 0x4008699: _dl_map_object (dl-load.c:2568) 
==27802== by 0x401384A: dl_open_worker (dl-open.c:225) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x4013319: _dl_open (dl-open.c:639) 
==27802== by 0x517F601: do_dlopen (dl-libc.c:89) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x517F6C3: __libc_dlopen_mode (dl-libc.c:48) 
==27802== by 0x4E423BB: pthread_cancel_init (unwind-forcedunwind.c:53) 
==27802== by 0x4E4257B: _Unwind_ForcedUnwind (unwind-forcedunwind.c:130) 
==27802== 
==27802== 312 bytes in 1 blocks are still reachable in loss record 3 of 4 
==27802== at 0x4C29DB4: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==27802== by 0x4010B59: _dl_check_map_versions (dl-version.c:300) 
==27802== by 0x4013E1F: dl_open_worker (dl-open.c:268) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x4013319: _dl_open (dl-open.c:639) 
==27802== by 0x517F601: do_dlopen (dl-libc.c:89) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x517F6C3: __libc_dlopen_mode (dl-libc.c:48) 
==27802== by 0x4E423BB: pthread_cancel_init (unwind-forcedunwind.c:53) 
==27802== by 0x4E4257B: _Unwind_ForcedUnwind (unwind-forcedunwind.c:130) 
==27802== by 0x4E4069F: __pthread_unwind (unwind.c:130) 
==27802== by 0x4E3AFF4: pthread_exit (pthreadP.h:265) 
==27802== 
==27802== 1,174 bytes in 1 blocks are still reachable in loss record 4 of 4 
==27802== at 0x4C29DB4: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==27802== by 0x400B57D: _dl_new_object (dl-object.c:77) 
==27802== by 0x4006805: _dl_map_object_from_fd (dl-load.c:1051) 
==27802== by 0x4008699: _dl_map_object (dl-load.c:2568) 
==27802== by 0x401384A: dl_open_worker (dl-open.c:225) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x4013319: _dl_open (dl-open.c:639) 
==27802== by 0x517F601: do_dlopen (dl-libc.c:89) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x517F6C3: __libc_dlopen_mode (dl-libc.c:48) 
==27802== by 0x4E423BB: pthread_cancel_init (unwind-forcedunwind.c:53) 
==27802== by 0x4E4257B: _Unwind_ForcedUnwind (unwind-forcedunwind.c:130) 
==27802== 
==27802== LEAK SUMMARY: 
==27802== definitely lost: 0 bytes in 0 blocks 
==27802== indirectly lost: 0 bytes in 0 blocks 
==27802==  possibly lost: 0 bytes in 0 blocks 
==27802== still reachable: 1,558 bytes in 4 blocks 
==27802==   suppressed: 0 bytes in 0 blocks 
==27802== 
==27802== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2) 
--27802-- 
--27802-- used_suppression:  2 dl-hack3-cond-1 
==27802== 
==27802== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2) 

कोड स्निपेट के लिए वेलग्रिंड उत्पादन के लिए

वेलग्रिंड उत्पादन 1 (कोई मेम-रिसाव नहीं, कुछ रन बाद में)

--29170-- Discarding syms at 0x64168d0-0x6426198 in /lib/x86_64-linux-gnu/libgcc_s.so.1 due to munmap() 
==29170== 
==29170== HEAP SUMMARY: 
==29170==  in use at exit: 0 bytes in 0 blocks 
==29170== total heap usage: 105 allocs, 105 frees, 28,814 bytes allocated 
==29170== 
==29170== All heap blocks were freed -- no leaks are possible 
==29170== 
==29170== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2) 
--29170-- 
--29170-- used_suppression:  2 dl-hack3-cond-1 
==29170== 
==29170== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2) 
+0

वाल्ग्रिंड आउटपुट क्या है? (इसके अलावा: उस कोड में कहीं भी 'pthread_exit' का उपयोग करने की कोई आवश्यकता नहीं है, आप केवल' 0 लौट सकते हैं; 'इसके बजाय। –

+0

वापसी 0 के साथ आपके सुधार को लागू किया; मुख्य() में pthread_exit() के बजाय। इसके अलावा, मैंने शामिल() कोड स्निपेट के लिए वाल्ग्रिंड आउटपुट जोड़ा, मुख्य() के पूरा होने का समय बढ़ाकर, जैसा कि मैंने दूसरे और तीसरे प्रश्नों को मंजूरी दे दी है। – swappy

+0

यह वास्तव में एक स्मृति रिसाव नहीं है, सभी मेमोरी अभी भी पहुंच योग्य है –

उत्तर

5

आपके धागे अलग होने पर एक बग है, अपरिभाषित व्यवहार का कारण बनता है। जो तुम संकेत के हाथ अपने कार्यकर्ता धागे को

struct args_for_job_t args[MAX_THREADS]; 

:

मुख्य में आप कोड की इस पंक्ति है।

तब मुख्य() इस भाग

pthread_exit(NULL); 

और मुख्य() अस्तित्व समाप्त हो जाता पहुँचता है, लेकिन आप अभी भी कार्यकर्ता धागे के आसपास हो सकता है, कि पहुँचता ऊपर args सरणी के ढेर पर है कि मुख्य() - जो अब अस्तित्व में नहीं है। आपके कार्यकर्ता धागे कुछ रनों में मुख्य() समाप्त होने से पहले खत्म हो सकते हैं, लेकिन अन्य रनों में नहीं।

+2

धन्यवाद, मैं इस तरह के व्यवहार पर संदेह कर रहा था लेकिन दोबारा जांचना चाहता था। बात यह है कि मैंने pthread_exit (NULL) से पहले एक टाइमर (usleep()) जोड़ने की कोशिश की (या जोनाथन सुझाव के रूप में 0 लौटा) और यह अभी भी यादृच्छिक रूप से व्यवहार किया। मैं इस धारणा के तहत रह रहा था कि वापसी के बजाय pthread_exit() का उपयोग करने से मुख्य() थ्रेड लटकाएगा जब तक कि सभी श्रमिकों को पूरा नहीं किया जाता है (वास्तव में क्या pthread_join() करता है)। – swappy

+0

मैं सही खड़ा हूं, मैंने नींद के समय को 10 000 से 100 000 तक अलग थ्रेड के साथ टक्कर लगी है और ऐसा लगता है कि सभी मेमोरी लीक चले गए हैं, यह सभी धागे को मुख्य() समाप्त होने से पहले पूरा करने के लिए पर्याप्त समय देता है। – swappy

+0

क्षमा करें, आप सही हैं कि 'main' में 'pthread_exit' इसे प्रतीक्षा करने का कारण बनता है। अन्य धागे और स्निपेट 1 में 'pthread_exit' को कॉल अनावश्यक –

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