Home > database >  C std::thread termination before join
C std::thread termination before join

Time:11-24

I am writing a C code to do a task on different threads and await the result to return to the main function.

Each thread can take different timing and might result in a correct or incorrect result.

I start the threads as follows:

std::thread p1(&process1, (void*)&p1Data);
std::thread p2(&process2, (void*)&p2Data);
std::thread p3(&process3, (void*)&p3Data);
.
.
.

(Multiple threads are created this way).

The Process Functions can be summarized like this:

\\ThreadData is a structure that contains all the data for the thread
void process1(void *data)
{
    ThreadData * tData = (ThreadData*) data;
    ResultObject * result = data->result;
    .
    .
    .
    \\Process to calculate Result using data
}

in main after I launch those threads I join them to await final results and then check the correct one.

p1.join();
p2.join();
.
.
.

p1result = p1Data.result;
p2result = p2Data.result;
.
.
.
.
.
.

if (p1result)
{
    return p1Data;
}

if (p2result)
{
    return p2Data;
}


But the issue is (for sake of simplicity I will consider that p1 is the fastest thread) if p1 is done with correct result, I am still forced to wait the join for all the other threads to get my final result while p1 that was done first can contain the correct one and I can just terminate on it.

How can I check on each thread when it is done if the result is ok so that I can terminate the remaining threads without the need on waiting on them to be done (ie if p2 finished before p1 and the result of p2 is acceptable, i can return p2Data and just terminate all the remaining threads without)

CodePudding user response:

Unfortunately there is no std::shared_promise that would have atomic set_if_still_empty() and is_empty() methods. Furthermore threads cannot be terminated from the outside (that's a good thing), C 20 solves it with std::jthread and its stop tokens, feel free to use that. I will use just std::atomic_bool flag. In both cases the thread itself must monitor the token and terminate itself if requested.

My solution uses std::promise to store the result, it will throw if the result has been set already and its setter is atomic, so no extra lock needed.

#include <future>
#include <thread>
#include <atomic>

using result_t=int;

void worker(std::promise<result_t>& promise,std::atomic_bool& stop_requested){
    //periodically check flag
    while(!stop_requested){
        // do work
        
        // if result
        result_t result=1;
        try{
            promise.set_value(result);
            // Terminate other threads soon, including us.
            stop_requested=true;
            return; // Not need if there is no work after `try` statement.
        }
        catch(const std::future_error&)//Already set
        {
            return;

        }     
    }
}

int main() {

    std::promise<result_t> promise;
    std::atomic_bool stop_token= false;

    std::thread w1(worker,std::ref(promise),std::ref(stop_token));

    auto future = promise.get_future();
   
    result_t result = future.get();
    // All threads should terminated soon after they check the token.
    //stop_token=true;// Is not necessary because it is set by the winning thread.

    w1.join();
    
}

Alternatively, std::optional<result_t> lock could be used too. But you would have to lock periodically to check whether the result is there and the thread should exit.

CodePudding user response:

You need to implement a synchronization mechanism within the threads themselves,using a mutex.

When a thread terminate, it can tell the other threads they can terminate early (using std::terminate within the thread) by just locking the mutex, writing the results and releasing the mutex. Each thread then have to look up if they can terminate early at regular intervals.

See: How do I terminate a thread in C 11?

  • Related