-1

I'm a new C++ coder and met a problem.

Please view the following code as an example:

#include <iostream>
#include <vector>
#include <thread>
class student{
    public: 
        std::string name;
        int id;
};
void foo(){
    int k = 1;
    std::cout<<"thread a"<<std::endl;
    return;
}
void foo2(){
    int k = 1;
    std::cout<<"thread b"<<std::endl;
    return;
}
void foo3(){
    std::thread a(foo);
    std::thread b(foo2);
    a.detach();
    b.detach();
    return;
}
int main(){
    foo3();
    return 0;
}

That's an example code. I want two threads running independently and release memory after each one ends. I use valgrind to check memory leak. It shows an error like this:

==63346== Memcheck, a memory error detector
==63346== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==63346== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==63346== Command: ./a.out
==63346== 
==63346== 
==63346== HEAP SUMMARY:
==63346==     in use at exit: 608 bytes in 4 blocks
==63346==   total heap usage: 5 allocs, 1 frees, 73,312 bytes allocated
==63346== 
==63346== 288 bytes in 1 blocks are possibly lost in loss record 3 of 4
==63346==    at 0x484DA83: calloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==63346==    by 0x40147D9: calloc (rtld-malloc.h:44)
==63346==    by 0x40147D9: allocate_dtv (dl-tls.c:375)
==63346==    by 0x40147D9: _dl_allocate_tls (dl-tls.c:634)
==63346==    by 0x4B4D834: allocate_stack (allocatestack.c:430)
==63346==    by 0x4B4D834: pthread_create@@GLIBC_2.34 (pthread_create.c:647)
==63346==    by 0x494A388: std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.30)
==63346==    by 0x1095B9: std::thread::thread<void (&)(), , void>(void (&)()) (in /home/alan/Avionics/test/a.out)
==63346==    by 0x10935C: foo3() (in /home/alan/Avionics/test/a.out)
==63346==    by 0x109400: main (in /home/alan/Avionics/test/a.out)
==63346== 
==63346== 288 bytes in 1 blocks are possibly lost in loss record 4 of 4
==63346==    at 0x484DA83: calloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==63346==    by 0x40147D9: calloc (rtld-malloc.h:44)
==63346==    by 0x40147D9: allocate_dtv (dl-tls.c:375)
==63346==    by 0x40147D9: _dl_allocate_tls (dl-tls.c:634)
==63346==    by 0x4B4D834: allocate_stack (allocatestack.c:430)
==63346==    by 0x4B4D834: pthread_create@@GLIBC_2.34 (pthread_create.c:647)
==63346==    by 0x494A388: std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.30)
==63346==    by 0x1095B9: std::thread::thread<void (&)(), , void>(void (&)()) (in /home/alan/Avionics/test/a.out)
==63346==    by 0x109372: foo3() (in /home/alan/Avionics/test/a.out)
==63346==    by 0x109400: main (in /home/alan/Avionics/test/a.out)
==63346== 
==63346== LEAK SUMMARY:
==63346==    definitely lost: 0 bytes in 0 blocks
==63346==    indirectly lost: 0 bytes in 0 blocks
==63346==      possibly lost: 576 bytes in 2 blocks
==63346==    still reachable: 32 bytes in 2 blocks
==63346==         suppressed: 0 bytes in 0 blocks
==63346== Reachable blocks (those to which a pointer was found) are not shown.
==63346== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==63346== 
==63346== For lists of detected and suppressed errors, rerun with: -s
==63346== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

If i replace detach() by join(), there's no memory leak, I'm a little confused. Does anyone can give me some advice? Thanks a lot!

Mary
  • 205
  • 1
  • 2
  • 6
Alanyy
  • 23
  • 6
  • 4
    Is there a reason that you want to use `detach()` instead of `join()`? – Drew Dormann Feb 06 '23 at 22:54
  • Dare I ask the purpose of the seemingly useless `class student` in this code? – WhozCraig Feb 06 '23 at 22:58
  • 2
    `detach()` pretty much means "I want to leak this thread and its resources on purpose". – Jonathan S. Feb 06 '23 at 22:58
  • 1
    [A detached thread causes memory leaks](https://stackoverflow.com/questions/20893358/a-detached-pthread-causes-memory-leaks). Detaching a thread explicitly means that you do not care if or when that thread finishes and releases it resources. – Drew Dormann Feb 06 '23 at 22:58
  • 2
    Never detach a thread unless you implement your own means of terminating it. It is not intended that threads are just forgotten about. Detaching allows a user to implement more advanced thread control - it is not there to avoid thread control altogether. – Galik Feb 06 '23 at 23:19
  • You cannot `return` from `main` until your code is done because that is defined to cause the process to terminate. – David Schwartz Feb 07 '23 at 02:43
  • @WhozCraig I forgot to delete that part of code. – Alanyy Feb 07 '23 at 15:11

1 Answers1

3

You need to make your main wait until the threads have finished running (or at least that they won't access any global objects after). If you don't make sure of that, not only will you have memory leaks, you may also have undefined behavior.

join does exactly make sure of that. The thread calling it will wait until the thread on which join is called finishes.

On the other hand detach is used to when you don't want to keep the std::thread object around to call join on it, while still having the thread function continue to run. If you detach a thread you need to use some other synchronization mechanism outside the join mechanism to make sure that main doesn't exit while the threads are still doing work (or at least accessing global objects).

There is rarely a need to detach a thread. Making it work is complicated. You don't need to do that.

Instead store the std::thread object somewhere and call join on it when you want to wait for your threads to end, at latest before main returns. That should be the default approach if you don't have a good reason to not use it. If you are a beginner, then you can forget about detach for now.

user17732522
  • 53,019
  • 2
  • 56
  • 105