Why having jobs and waits for them in other thread cause whole application to wait?

  c++, multithreading

I’ve got a thread pool

struct ThreadPool
{
    using Task = std::packaged_task<void()>;

    explicit ThreadPool(size_t workersCount)
    {
        workers.reserve(workersCount);
        for(uint32_t i = 0u; i < workersCount; ++i) {
            workers.emplace_back([=]() {
                while(true) {
                    Task result;
                    {
                        std::unique_lock<std::mutex> locker(mutex);
                        condition.wait(locker, [=]() { return stop || !tasks.empty(); });

                        if(stop && tasks.empty()) {
                            break;
                        }

                        result = std::move(tasks.front());
                        tasks.pop();
                    }

                    result();
                }
            });
        }
    }

    ~ThreadPool() noexcept
    {
        stop = true;

        condition.notify_all();

        for(auto& worker : workers) {
            worker.join();
        } workers.clear();
    }

    template<typename T>
    inline auto Enqueue(T task)->std::future<decltype(task())>
    {
        auto package = std::packaged_task<decltype(task())()>(std::move(task));
        auto result = package.get_future();
        {
            std::unique_lock<std::mutex> locker(mutex);
            tasks.emplace(std::move(package));
        }

        condition.notify_one();

        return result;
    }

    std::vector<std::thread> workers;

    std::queue<Task> tasks;
    std::mutex mutex;
    std::condition_variable condition;
    std::atomic_bool stop = false;
};

and this example

//just for the example this is a global
static ThreadPool pool{4};

struct DoSomethingStruct
{
    void DoSomething()
    {
        std::vector<std::future<void>> futures;

        for(uint32_t i = 0; i < 10; ++i) {
            futures.push_back(pool.Enqueue([this, i]() {
                ints.push_back(i);
            }));
        }

        for(const auto& future : futures) {
            future.wait();
        }
    }

    std::vector<int> ints;
};

int main()
{
    DoSomethingStruct dss;

    std::vector<std::future<void>> futures;

    for(uint32_t i = 0; i < 10; ++i) {
        futures.push_back(pool.Enqueue([&dss, i]() {
            dss.DoSomething();
        }));
    }

    for(const auto& future : futures) {
        future.wait();
    }

    return 0;
}

When I run the application it never ends.
The example above does not actually present an actual use case. I am wondering why it does not wait for 10 futures in DoSomethingStruct::DoSomthing(); and then in main for 10 other jobs.

I wanted to do something similar to what this guy did https://wickedengine.net/2018/11/24/simple-job-system-using-standard-c/, but with futures and mutex and condition variable.

Why is that? What did I do wrong?

Source: Windows Questions C++

LEAVE A COMMENT