A way to destroy “thread” class

前端 未结 8 1251
甜味超标
甜味超标 2021-02-06 18:22

Here is a skeleton of my thread class:

class MyThread {
public:
   virutal ~MyThread();

   // will start thread with svc() as thread entry point
   void start()         


        
相关标签:
8条回答
  • 2021-02-06 18:31

    You first need a way to communicate with the thread to tell it to shut down. The best mechanism to do this depends on what svc() is doing. If, for example, it is looping on a message queue, you could insert a "please stop" message in that queue. Otherwise, you could simply add a member bool variable (and synchronize access to it) that is periodically checked by the svc(), and set by the thread wanting to destroy the object. Your could add a pure virtual stop() function to your base class, giving the implementor a clear signal that it has to implement svc() to make its class "runnable", and to implement stop() to make it "stoppable".

    After asking the thread to stop, you must wait for it to exit before destroying the object. Again, there are several ways to do this. One is to make the stop() function blocking. It could wait, for example, for a "ok, I'm really stopped now" condition variable to be set by the thread running svc(). Alternatively, the caller could "wait" on the thread running svc(). The way to "wait" is platform dependent.

    0 讨论(0)
  • 2021-02-06 18:33

    I reckon the easiest way to do this is to wrap the thread execution code in a loop

    while(isRunning())
    {
         ... thread implementation ...
    }
    

    You can also stop your thread by doing specific calls, for instance when you're using a WIN32 thread you can call TerminateThread on the thread handle in the destructor.

    0 讨论(0)
  • 2021-02-06 18:41

    Usually any OS-specific threads API will allow you to "join" a thread. That is, to block indefinitely on a thread handle until the thread functions returns.

    So,

    1. Signal the thread function to return (e.g. by setting a flag in its loop to false).
    2. Join the thread, to make sure the actual thread terminates before you try to delete the thread object.
    3. Then you can proceed with destruction of the thread object (you may also join in the destructor, though some people object to blocking destructors.).

    I've had a project before with a similar "thread worker" class and a corresponding "work item" class (a-la Java's Thread and Runnable, except thread does not terminate but waits for a new Runnable object to be executed).

    In the end, there was no difference if you join in a separate "shutdown" function or in the destructor, except a separate function is a bit more clear.

    1. If you join in a destructor and a thread blocks, you will wait indefinitely.
    2. If you join in a separate function and a thread blocks, you will wait indefinitely.
    3. If you detach the thread and let it finish on its own, it will usually block application from exiting, so you will wait indefinitely.

    So there is no straightforward way to make a thread behave like a regular C++ object and ignore its OS thread semantics, unless you can guarantee that your thread code can terminate almost immediately when notified to do so.

    0 讨论(0)
  • 2021-02-06 18:41

    Most thread systems allow you to send a signal to a thead.

    Example: pthreads

    pthread_kill(pthread_t thread, int sig);
    

    This will send a signall to a thread. You can use this to kill the thread. Though this can leave a few of the resources hanging in an undefined state.

    A solution to the resource problem is to install a signall handler.
    So that when the signal handler is called it throws an exception. This will cause the thread stack to unwind to the entry point where you can then get the thread to check a variable about weather it is sill alive.

    NOTE: You should never allow an exception to propogate out of a thread (this is so undefined my eyes bleed thinking about it). Basically catch the exception at the thread entry point then check some state variable to see if the thread should really exit.

    Meanwhile the thread that sends the signal should wait for the thread to die by doing a join.

    The only issues are that when you throw out of signal handler function you need to be careful. You should not use a signal that is asynchronus (ie one that could have been generated by a signal in another thread). A good one to use is SIGSEGV. If this happens normally then you have accessed invalid memory any you thread should think about exiting anyway!

    You may also need to specify an extra flag on some systems to cope.
    See This article

    A working example using pthreads:

    #include <pthread.h>
    #include <iostream>
    
    extern "C" void* startThread(void*);
    extern "C" void  shouldIexit(int sig);
    
    class Thread
    {
        public:
            Thread();
            virtual ~Thread();
        private:
            friend void* startThread(void*);
    
            void start();
            virtual void run() = 0;
    
            bool        running;
            pthread_t   thread;
    };
    
    
    // I have seen a lot of implementations use a static class method to do this.
    // DON'T. It is not portable. This is because the C++ ABI is not defined.
    //
    // It currently works on several compilers but will break if these compilers
    // change the ABI they use. To gurantee this to work you should use a
    // function that is declared as extern "C" this guarantees that the ABI is 
    // correct for the callback. (Note this is true for all C callback functions)
    void* startThread(void* data)
    {
        Thread* thread  = reinterpret_cast<Thread*>(data);
        thread->start();
    }
    void shouldIexit(int sig)
    {
        // You should not use std::cout in signal handler.
        // This is for Demo purposes only.
        std::cout << "Signal" << std::endl;
    
        signal(sig,shouldIexit);
        // The default handler would kill the thread.
        // But by returning you can continue your code where you left off.
        // Or by throwing you can cause the stack to unwind (if the exception is caught).
        // If you do not catch the exception it is implementation defined weather the
        // stack is unwound.
        throw int(3);  // use int for simplicity in demo
    }
    
    
    Thread::Thread()
        :running(true)
    {
        // Note starting the thread in the constructor means that the thread may
        // start before the derived classes constructor finishes. This may potentially
        // be a problem. It is started here to make the code succinct and the derived
        // class used has no constructor so it does not matter.
        if (pthread_create(&thread,NULL,startThread,this) != 0)
        {
            throw int(5); // use int for simplicity in demo.
        }
    }
    
    Thread::~Thread()
    {
        void*   ignore;
    
        running = false;
        pthread_kill(thread,SIGSEGV); // Tell thread it may want to exit.
        pthread_join(thread,&ignore); // Wait for it to finish.
    
        // Do NOT leave before thread has exited.
    
        std::cout << "Thread Object Destroyed" << std::endl;
    }
    
    void Thread::start()
    {
        while(running)
        {
            try
            {
                this->run();
            }
            catch(...)
            {}
        }
        std::cout << "Thread exiting" << std::endl;
    }
    class MyTestThread:public Thread
    {
        public:
            virtual void run()
            {
                // Unless the signal causes an exception
                // this loop will never exit.
                while(true)
                {
                    sleep(5);
                }
            }
    
    };
    
    struct Info
    {
         Info() {std::cout << "Info" << std::endl;}
        ~Info() {std::cout << "Done: The thread Should have exited before this" << std::endl;}
    };
    
    int main()
    {
        signal(SIGSEGV,shouldIexit);
    
        Info                info;
        MyTestThread        test;
    
        sleep(4);
        std::cout << "Exiting About to Exit" << std::endl;
    
    }
    
    
    > ./a.exe
    Info
    Exiting About to Exit
    Signal
    Thread exiting
    Thread Object Destroyed
    Done: The thread Should have exited before this
    >
    
    0 讨论(0)
  • 2021-02-06 18:43

    i give a simple and clean design, no signal, no sync, no kill needed.

    per your MyThread, i suggest renaming and adding as below:

    class MyThread { 
    public: 
       virutal ~MyThread(); 
    
       // will be called when starting a thread, 
       // could do some initial operations 
       virtual bool OnStart() = 0;  
    
       // will be called when stopping a thread, say calling join().
       virtual bool OnStop() = 0;
    
       // derive class will specialize what the thread should do, 
       // say the thread loop such as 
       // while (bRunning) {
       //    do the job.
       // } 
       virtual int OnRun() = 0;                 
    }; 
    

    the thread interface user will control the lifetime of MyThread.

    and actually the real thread object is as below:

        class IThread
        {
        public:
            virtual API ~IThread() {}
    
            /* The real destructor. */
            virtual void Destroy(void) = 0;
    
            /* Starts this thread, it will call MyThread::OnStart() 
                 * and then call MyThread::OnRun() just after created 
             *   the thread. */
            virtual bool Start(void) = 0;
    
            /* Stops a thread. will call MyThread::OnStop(). */
            virtual void Stop(void) = 0;
    
            /* If Wait() called, thread won't call MyThread::OnStop().
             * If could, it returns the value of MyThread::OnRun()
             *   returned */
            virtual int Wait(void) = 0;
    
            /* your staff */
            virtual MyThread * Command(void) = 0;
    
        };
    
    /* The interface to create a thread */
    extern IThread * ThrdCreate(MyThread *p);
    

    See the complete interfaces

    http://effoaddon.googlecode.com/svn/trunk/devel/effo/codebase/addons/thrd/include/thrd_i.h
    

    Coding Examples

    Case 1. Controlled thread loop

    class ThreadLoop : public MyThread
    {
    private:
       bool m_bRunning;
    public:
       virtual bool OnStart() { m_bRunning = true; }  
    
       virtual bool OnStop() { m_bRunning = false; }
    
       virtual int OnRun() 
       {
             while (m_bRunning) {
                  do your job;
             }
       }                 
    };
    
    int main(int argc, char **argv)
    {
          ThreadLoop oLoop;
    
          IThread *pThread = ThrdCreate(&oLoop);
          // Start the thread, it will call Loop::OnStart() 
          //and then call Loop::OnRun() internally.
          pThread->Start();
          do your things here. when it is time to stop the thread, call stop().
          // Stop the thread, it will call Loop::OnStop(), 
          // so Loop::OnRun() will go to the end
          pThread->Stop();
          // done, destroy the thread
          pThread->Destroy();
    }
    

    Case 2. Don't know when the thread will stop

    class ThreadLoop : public MyThread
    {
    public:
       virtual bool OnStart() {  }  
    
       virtual bool OnStop() { }
    
       virtual int OnRun() 
       {
             do your job until finish.
       }                 
    };
    
    int main(int argc, char **argv)
    {
          ThreadLoop oLoop;
    
          IThread *pThread = ThrdCreate(&oLoop);
          // Start the thread, it will call Loop::OnStart() 
          //and then call Loop::OnRun() internally.
          pThread->Start();
          do your things here. Since you don't know when the job will 
          finish in the thread loop. call wait().
          // Wait the thread, it doesn't call Loop::OnStop()
          pThread->Wait();
          // done, destroy the thread
          pThread->Destroy();
    }
    

    A complete IThread implementation:

    see

    http://effoaddon.googlecode.com/svn/trunk/devel/effo/codebase/addons/thrd/src/thrd/thrd.cpp
    
    0 讨论(0)
  • 2021-02-06 18:45

    You could havee somthing like this in your svc method

    while (alive){ //loops}
    //free resources after while.
    

    In your destructor, you could set the alive member to false. Or, you could have a pleaseDie() method, that sets the alive member to false, and can be called from the outside requesting the Thread instance to stop processing.

    void
    Thread::pleaseDie()
    {
    
     this->alive = false;
    }
    
    0 讨论(0)
提交回复
热议问题