Threads won't detach

I am trying to create a thread pool. I have std::unordered_map

one that maps thread ids to std::thread

( workers

). Thread IDs are thread IDs waiting for a task to be written to the thread pool ( waiters

). Tasks are presented std::stack

( tasks

). When a task is popped into the pool, the task is popped off the stack and becomes a thread function as the "value" of a portion of the map.

In the destructor, I am trying to shutdown all threads that are still running. But I am still getting the following exception:

terminate called without an active exception
bash: line 7: 16881 Aborted                 (core dumped) ./a.out

      

This means that the threads were not disconnected and the program terminated. But my destructor goes through the elements and separates them (I believe). Why is this happening and how can I fix it?

#include <queue>
#include <stack>
#include <mutex>
#include <thread>
#include <algorithm>
#include <functional>
#include <type_traits>
#include <unordered_map>
#include <condition_variable>

template <class F>
class thread_pool
{
    static_assert(std::is_function<F>::value, "F must have function type");
public:
    thread_pool();
    ~thread_pool();
    template <class Task>
    void push(Task&&);
private:
    std::unordered_map<std::thread::id, std::thread> workers;
    std::queue<std::thread> waiters;
    std::stack<std::function<F>> tasks;
    static std::size_t max;
private:
    std::condition_variable m_cond;
    std::mutex m;
private:
    void wait_for_tasks();
};

template <class F>
std::size_t thread_pool<F>::max(10);

template <class F>
thread_pool<F>::thread_pool()
{
    std::lock_guard<std::mutex> lock(m);
    for (std::size_t i = 0; i < max; ++i)
        waiters.emplace(&thread_pool<F>::wait_for_tasks, this);
}

template <class F>
void thread_pool<F>::wait_for_tasks()
{
    while (true)
    {
        std::unique_lock<std::mutex> lock(m);
        m_cond.wait(lock, [this] { return !tasks.empty(); });

        auto f = tasks.top();
        tasks.pop();
        auto& th = workers[std::this_thread::get_id()];

        if (th.get_id() == std::thread::id())
            th = std::thread(f);
    }
}

template <class F>
template <class Task>
void thread_pool<F>::push(Task&& t)
{
    {
        std::lock_guard<std::mutex> lock(m);
        tasks.emplace(std::forward<Task>(t));
    }
    m_cond.notify_all();
}

template <class F>
thread_pool<F>::~thread_pool()
{
    std::for_each(workers.begin(), workers.end(), [] (std::pair<std::thread::id const, std::thread>& p)
    {
        if (p.second.joinable())
            p.second.detach();
    });

    while (!waiters.empty())
    {
        auto& t = waiters.front();
        waiters.pop();
        if (t.joinable())
            t.detach();
    }
}

int main()
{
    thread_pool<void ()> pool;
}

      

I'm not even sure if this is the best way to do it, this is my first time. Below is a demo .

+3


source to share


1 answer


In the pool destructor, you call pop

before detaching the thread, effectively destroying the thread that is connecting: the standard guarantees that this will callstd::terminate

Detach the stream first and then pull it out of the queue:



while (!waiters.empty())
{
    auto& t = waiters.front();

    if (t.joinable())
        t.detach();

    waiters.pop();
}

      

+5


source







All Articles