daniël de groot
daniël de groot

Reputation: 23

C++ thread can't abort after being joined

So I have 2 threads running in my program and I eventually want them to terminate when this function is called:

void AutoClicker::TerminateThreads()
{
    programrunning = false;
    clicker.join();
    hotkeyLoop.join();
    terminateThread = true;
}

And after that is called the threads are supposed to run this code:

while (1)
{
    while (programrunning)
    {
        mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
        mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
        std::this_thread::sleep_for(std::chrono::milliseconds(delay));
    }

    if (terminateThread) {
        std::terminate();
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
}

But the last if statement doesn't seem to be called. It is located inside the threads I am trying to terminate, so it should just work right? I am pretty new to threads so I don't really know how to do this, but this is what I found online:

"When a std::thread object gets destroyed it is not allowed to be joinable(), i.e., one of two things have to happen before it is destroyed:

It was detached and the thread has gone into the wild and there is pretty much no control over whether it has finished or not.

The thread has been join()ed."

Upvotes: 1

Views: 1647

Answers (3)

Frodyne
Frodyne

Reputation: 3983

Two things to start off with:

  • Do not call std::terminate(). It is not a nice way to tear down your program, and may leave open files and other OS resources hanging. Also, it applies to your entire program, not just the thread you call it in.
  • If you want a std::thread to terminate, then just return from it or allow it to reach the end of the thread function.

With that out of the way, here is a small program showing how I would do something like what you describe:

#include <iostream>
#include <atomic>
#include <chrono>
#include <thread>

void threadfunc(std::atomic<bool>& running)
{
    while (running) {
        std::cout << "beep\n";
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
}

void waitThread(std::atomic<bool>& running)
{
    std::this_thread::sleep_for(std::chrono::seconds(10));
    running = false;
}

int main()
{
    std::atomic<bool> running = true;

    std::thread t1(threadfunc, std::ref(running));
    std::thread t2(waitThread, std::ref(running));
    t1.join();
    t2.join();
}

Hopefully you can scavenge some bits here to make your program work.

EDIT: Clarifying some stuff from a comment OP made to another answer:

Something like: std::thread t1(threadfunc, std::ref(running)); creates an active thread that executes the provided function. Once that function stops (returns) the thread is left in a zombie state, where it is not executing but still active and holding resources.

https://en.cppreference.com/w/cpp/thread/thread/joinable

A thread that has finished executing code, but has not yet been joined is still considered an active thread of execution and is therefore joinable.

In order to pull the thread out of this zombie state and clean up its resources, you have to call join() on it. Note that you can call join() on that thread before OR after it finishes execution - if you join before, you block until the joined thread is done, if you join after, the lingering thread is cleaned up and you immediately continue.

Alternatively, you can call detach() on the thread:

https://en.cppreference.com/w/cpp/thread/thread/detach

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

But once you have detached a thread you can no longer join it, and having your main thread exit while there is still live-but-detached threads running kills the detached threads in a not-nice way. So only do that if you are sure the detached thread will finish before the main thread.

Upvotes: 2

Do you know what join does? join waits for the thread to finish.

Your TerminateThreads function sets programrunning to false, then waits for the threads to finish, then sets terminateThread to true.

The thread keeps running until terminateThread becomes true, so when the TerminateThreads function is called, it never gets to to the part where it sets terminateThread to true, because it's waiting for the threads to finish.

Upvotes: 1

Daniel McLaury
Daniel McLaury

Reputation: 4303

You don't really have all the code here, but I'm guessing the method in the clicker thread probably looks something like

void clickerThreadFunction()
{
  while(programrunning)
  {
    // do stuff
  }

  if (terminateThread)
  {
    std::terminate();
  }
}

If this is the case, then looking at the code

programrunning = false;
clicker.join();

the first line will cause the while loop in the clicker thread to exit. The rest of the clicker method will run, including the if (terminateThread) check, which will fail because you haven't set terminateThread to true. Only then, once we've hit the bottom of clickerThreadFunction, will clicker.join() return, because std::thread::join waits for a thread to finish and then returns control.

Anyway I'm not clear what you're trying to do here. The purpose of std::terminate is to immediately crash the program. If you want that to happen it's not clear why you're bothering with joining the threads since you wouldn't reach that part anyway.

Upvotes: 1

Related Questions