0

In C++ 11, I create a a long-live thread :

class Server
{
   public:
      void operator() () const {
         while(true)
            dosomething();
      }
      ~Server()
       {
         clean();
       }
};

// in main
Server s;
std::thread t(s);
t.detach();

I want add some clean work using a destructor when the system terminated. However, if I add a destructor in Server directly, the clean will be called when system starts.

How to do it?

chenzhongpu
  • 6,193
  • 8
  • 41
  • 79
  • 4
    I'd say that with `std::thread t(s);` you're creating a copy of the server object. The destructor you see would in that case be the destructor of the original server object when that goes out of scope somewhere after t.detach(). – Dan Mašek Apr 01 '16 at 05:49

1 Answers1

0

In his book (The C++ Programming Language, Chapter 42.2.5), Bjarne Stroustrup discourages from using detach if you don't have to and gives several reasons for that.

See also here: https://stackoverflow.com/a/22804813/5717589.

The consequence in this case is that the destructor of the Server trapped in the thread is not executed, cleanup() doesn't run.

I wrote the following code to see what's happening there:

#include <iostream>
#include <thread>
#include <unistd.h>   // for linux usleep

using namespace std;
static bool needed;   // tells the server if we need it

class Server {
public:
    Server () : id (0){
        ++id;
        cout << "Server " << id << " started. Work status: " << work_done << endl;
    }
    Server (const Server & rhs) : id (rhs.id){
        ++id;
        cout << "Server " << id << " started by copy. Work status: " << work_done << endl;
    }
    Server (Server && rhs) : id (rhs.id) {
        ++id;
        cout << "Server " << id << " started by move. Work status: " << work_done << endl;
    }

    void operator()() {
        cout << "Hi! Doing work..." << endl;
        work_done = true;
        while (needed) {
            usleep(300000);
            cout << "sleeping... Work status: " << work_done << endl;
        }
    }
    ~Server() {
        cout << "Server " << id << " stopping. Work status: " << work_done << endl;
        if (work_done)
            cout << "Cleanup triggered by Server " << id << endl;
    }
    bool work_done {false};
    int id;
};

int main() {
    needed = true;
    Server s;
//    thread t(s);
    thread t(std::move(s));
//    t.detach();
    usleep(1000000);
    needed = false;
    t.join();
    cout << "end" << endl;
}

The output is:

Server 1 started. Work status: 0
Server 2 started by move. Work status: 0
Server 3 started by move. Work status: 0
Server 2 stopping. Work status: 0
Hi! Doing work...
sleeping... Work status: 1
sleeping... Work status: 1
sleeping... Work status: 1
sleeping... Work status: 1
Server 3 stopping. Work status: 1
Cleanup triggered by Server 3
end
Server 1 stopping. Work status: 0

With t.detach() on and t.join() off the result is:

Server 1 started. Work status: 0
Server 2 started by move. Work status: 0
Server 3 started by move. Work status: 0
Server 2 stopping. Work status: 0
Hi! Doing work...
sleeping... Work status: 1
sleeping... Work status: 1
sleeping... Work status: 1
end
Server 1 stopping. Work status: 0

(What puzzles me is some temporary instance, Server 2, which appears both with move and copy.)

Community
  • 1
  • 1
ptrj
  • 5,152
  • 18
  • 31
  • needed is accessed synchronously by two threads, so this is undefined behavior. I think the std::move(s) to the thread function isn't necessary; just pass it a reference, don't detach, and call join before Server goes out of scope. – ech Feb 26 '19 at 18:49