Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Telling an std::thread to kill/stop itself when a condition is met

Say I have a worker thread tWorker, which is initialized when Boss is constructed and tells it to do work(), until bRetired is true. An std::mutex, mtx, locks some data (vFiles) so that tWorker owns it when he's working on it.

How do I make tWorker "commit suicide" once bRetired becomes true? How would the mutex be destroyed when the thread stops execution?

I've read that std::thread objects cannot be interrupted in any way. Does letting the thread do nothing (or calling std::this_thread::yield()) provide the same effect as killing the thread?

class Boss {
private:
    std::thread tWorker;
    std::mutex mtx;
    bool bRetired;
    std::vector< std::string > vFiles;

    void work() {
        while ( bRetired == false ) {
            // Do your job!
            mtx.lock();
            // ... Do something about vFiles ...
            mtx.unlock();
        }

        // tWorker has retired, commit suicide
        // ** How? **

        // Does this suffice if I want to "kill" the thread?
        std::this_thread::yield(); 
    }

public:
    Boss() {
        bRetired = false;
        tWorker = std::thread( &Boss::work, this );

        // Have worker do its job independently
        // **Bonus Question** : Should this be tWorker.join() or tWorker.detach()?
        tWorker.detach();
    }

    retire() {
        bRetired = true;
    }
}

Notes

  • The worker thread cannot be started again once it is retired.
  • The worker thread works on the background without interrupting the main thread's execution.
like image 253
alxcyl Avatar asked Sep 18 '13 09:09

alxcyl


People also ask

How do you force a thread to kill in C++?

You could call std::terminate() from any thread and the thread you're referring to will forcefully end. You could arrange for ~thread() to be executed on the object of the target thread, without a intervening join() nor detach() on that object.

How do you pause a thread in C++?

You can pause a thread by making it wait on a std::mutex .

How do you terminate a thread?

A thread automatically terminates when it returns from its entry-point routine. A thread can also explicitly terminate itself or terminate any other thread in the process, using a mechanism called cancelation.

What does thread detach do?

thread::detachSeparates the thread of execution from the thread object, allowing execution to continue independently. Any allocated resources will be freed once the thread exits.


2 Answers

How do I make tWorker "commit suicide" once bRetired becomes true?

You let the control flow exit the thread function. That std::this_thread::yield() call in unnecessary.

How would the mutex be destroyed when the thread stops execution?

That mutex is a member of Boss class. It gets destroyed in the destructor of Boss when the object is getting destroyed.

I've read that std::thread objects cannot be interrupted in any way.

C++ API does not provide means to terminate an arbitrary thread. There has to be a way to tell a thread to terminate and then wait till it does, as you intend to do.

Does letting the thread do nothing (or calling std::this_thread::yield()) provide the same effect as killing the thread?

No.

There is a race condition on bRetired variable though. It either needs to be std::atomic<bool> or it should only be read and modified only when that mutex is locked.

like image 83
Maxim Egorushkin Avatar answered Oct 18 '22 01:10

Maxim Egorushkin


The call to std::thread::yield() is unrequired and does not kill the calling thread:

Provides a hint to the implementation to reschedule the execution of threads, allowing other threads to run.

Just exit the function to exit the thread.

Note that the use of bRetired is incorrect as two threads can be accessing the same memory location and one of those threads is modifying it: this is undefined behaviour. Also, the change made in the function retire(), a different thread, will not be seen by the thread executing run(): use atomic<bool> for atomicity and visibility.

If join() was used within the constructor the constructor would not return until the thread exited, which would never happen as it would be impossible to call retire() because the object would not be available (as the constructor would not have returned). If it is required to synchronize with the exiting of the thread then do not detach() but join() in the retire() function:

void retire() {
    bRetired = true;
    tWorker.join();
}

Use RAII for acquiring mutexes (std::lock_guard for example) to ensure it always released. The mutex will be destroyed when it goes out of scope, in this case when its containing class is destructed.

like image 21
hmjd Avatar answered Oct 18 '22 02:10

hmjd