Ari Seyhun
Ari Seyhun

Reputation: 12531

Remove thread from vector upon thread completion

I have a vector of threads in my C++ program.

std::vector<std::thread> threadList;

I then create a thread and push it into the vector.

threadList.push_back(std::thread([]() { ... }));

How can I remove the thread from the threadList vector when the lambda function finishes execution?


Edit

I've come up with somewhat of a solution; Before the thread lambda function returns, it iterates through the vector to find a match in ID with this_thread::get_id().

Through Visual Studio debugging line by line, I see that it finds a match in ID and the erase function is executed, but as soon as threadList.erase(threadList.begin() + index); is executed, I come accross an unhandled exception at the thread's deconstructor function.

I've written a small piece of code which replicates this error.

vector<thread> threadList;

threadList.push_back(thread([]() {
    Sleep(1000);
    threadList.erase(threadList.begin());
}));

Sleep(2000);

//for_each(threadList.begin(), threadList.end(), mem_fn(&thread::detach));
//threadList.clear();

This code results in the screenshot below.

enter image description here

Upvotes: 4

Views: 5111

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 597670

One option is to have the lambda remove the thread asynchronously when exiting. For example:

std::vector<std::thread> threadList;
std::mutex threadMutex;

... 

void removeThread(std::thread::id id)
{
    std::lock_guard<std::mutex> lock(threadMutex);
    auto iter = std::find_if(threadList.begin(), threadList.end(), [=](std::thread &t) { return (t.get_id() == id); });
    if (iter != threadList.end())
    {
        iter->detach();
        threadList.erase(iter);
    }
}

... 

{
    std::lock_guard<std::mutex> lock(threadMutex);
    threadList.push_back(
        std::thread([]() {
            ...
            std::async(removeThread, std::this_thread::get_id());
        })
    );
}

Alternatively:

std::vector<std::thread> threadList;
std::mutex threadMutex;

... 

void removeThread(std::thread::id id)
{
    std::lock_guard<std::mutex> lock(threadMutex);
    auto iter = std::find_if(threadList.begin(), threadList.end(), [=](std::thread &t) { return (t.get_id() == id); });
    if (iter != threadList.end())
    {
        iter->join();
        threadList.erase(iter);
    }
}

... 

{
    std::lock_guard<std::mutex> lock(threadMutex);
    threadList.push_back(
        std::thread([]() {
            ...
            std::thread(removeThread, std::this_thread::get_id()).detach();
        })
    );
}

Alternatively:

std::vector<std::thread> threadList;
std::mutex threadMutex;

std::list<std::thread::id> threadFreeList;
std::mutex threadFreeMutex;
std::condition_variable threadFreeCV;

std::thread monitor([]() {
    while (... /* app is not terminated */)
    {
        std::unique_lock<std::mutex> lock(threadFreeMutex);
        threadFreeCV.wait(lock);

        std::lock_guard<std::mutex> lock2(threadMutex);
        auto iter = threadFreeList.begin();
        while (iter != threadFreeList.end())
        {
            auto id = *iter;
            auto found = std::find_if(threadList.begin(), threadList.end(), [=](std::thread &t) { return (t.get_id() == id); });
            if (found != threadList.end())
            {
                found->join();
                threadList.erase(found);
            }
            iter = threadFreeList.erase(iter);
        }
    } 
});

...

{
    std::lock_guard<std::mutex> lock(threadMutex);
    threadList.push_back(
        std::thread([]() {
            ...
            std::unique_lock<std::mutex> lock(threadFreeMutex);
            threadFreeList.push_back(std::this_thread::get_id());
            threadFreeCV.notify_one();
        })
    );
} 

Upvotes: 4

Evgeny Lazin
Evgeny Lazin

Reputation: 9423

Why do you need this vector of threads when you can detach them?

// Scope that outlives the threads
boost::barrier out_barrier(N_threads+1);

...

// Starting the threads
for(int i = 0; i < N_threads; i++) {
    std::thread th([&out_barrier]() {
        ...do the job...
        out_barrier.wait();
    });
    th.detach();
}

...

// Wait for threads to finish
out_barrier.wait();

The threads are not joinable so it's safe to call the destructor. The synchronization is unavoidable in this case. In my example, it's used to join all threads and if you have a vector of threads you will need to synchronize the access to the vector so it's all the same.

Upvotes: 0

Related Questions