2
votes

I want the main function to stop function_ when an event occurs (after 1ms in this example). The problem I have is that function_ immediately relock the mutex without letting the main function to get it.

#include <thread>
#include <mutex>
#include <iostream>
#include <chrono>

using namespace std;

void function_(volatile bool *ptrStop, mutex *ptrMtx) {
    for (int i = 0; i < 10; i++) {
        ptrMtx->lock();
        if (*ptrStop) {
            ptrMtx->unlock();
            return;
        }
        //doSomething();
        this_thread::sleep_for(chrono::milliseconds(1));
        cout << "Iteration " << i << endl;
        ptrMtx->unlock();
        //this_thread::sleep_for(chrono::milliseconds(1));
    }
    return;
}


int main() {
    volatile bool stop = 0;
    mutex mtx;

    thread functionThread(function_, &stop, &mtx);

    this_thread::sleep_for(chrono::milliseconds(1));
    mtx.lock();
    stop = 1;
    mtx.unlock();
    cout << "Changed boolean variable value" << endl;
    functionThread.join();

    system("pause");
    return 0;
}

I got the following output :

Iteration 0
Iteration 1
Iteration 2
Iteration 3
Iteration 4
Iteration 5
Iteration 6
Iteration 7
Iteration 8
Iteration 9
Changed boolean variable value

What I want is to only do 1 or 2 iterations of function_only (therefor let the main take the mutex). How can I do that ? I heard about the std::condition_variable but I couldn't figure a way to make what I want.

Moreover, if doSomething() is uncommented and takes a long time to return, is there an easy way to kill the thread or force it to join without modifying what is in doSomething function ?

2
Can you show what you tried with std::condition_variable? It should be what you want to use.NathanOliver
Volatile is not intended for multi-threading purposes (stackoverflow.com/questions/35345899/…).Jens
Right now the thread could complete run all iterations of function_ before mtx.lock() is reached in the main thread.Jens
Did you read the documentation of std::condition_variable? It should be easy to extend the example such that it does what you want. You just need a stop flag and a loop in the worker thread.Jens
Don't use lock and unlock. Use either std::unique_lock or std::lock_guard as appropriate.Jive Dadson

2 Answers

2
votes

You don't need all that mechanism. An atomic<bool> and join will suffice.

Don't use lock and unlock. Use either unique_lock or lock_guard as appropriate.

Don't use volatile unless you are dealing with memory-mapped hardware or unix signal-handlers.

SPOILER ----

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

using namespace std;  // Kids, don't try this at home.

using stopper = atomic<bool>;

void function_( stopper *ptrStop) {
    for (int i = 0; i < 10; i++) {
        if (*ptrStop) {
            break;
        }
        //doSomething();
        this_thread::sleep_for(chrono::milliseconds(10));
        cout << "Iteration " << i << endl;
        //this_thread::sleep_for(chrono::milliseconds(1));
    }
    *ptrStop = false;
    return;
}


int main() {
    stopper stop{ false };
    thread functionThread(function_, &stop);

    this_thread::sleep_for(chrono::milliseconds(100));

    stop = true;
    // // The following is optional
    ///while (stop == true) {
    //    this_thread::yield();
    //}
    cout << "Changed boolean variable value" << endl;
    functionThread.join();

    return 0;
}
1
votes

I'm just going to answer this question:

Moreover, if doSomething() is uncommented and takes a long time to return, is there an easy way to kill the thread or force it to join without modifying what is in doSomething function?

The answer is no. If you try and kill the thread in any way, you can not be sure that it's been killed at a time when it was harmless to other threads to do so. For example; your c++ library could implement new with a mutex; if you're in the middle of creating a new object when your thread is killed; the mutex will remain locked for ever; leaving all other threads unable to call new. This will result in your program deadlocking in a way that you'll never be able to debug.

The only way you can do this reliably is do a little work in doSomething(); and then check to see if you can go back and do more.