Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

线程池的实现中存在错误 #24

Closed
Mq-b opened this issue Aug 3, 2024 · 0 comments
Closed

线程池的实现中存在错误 #24

Mq-b opened this issue Aug 3, 2024 · 0 comments
Labels
bug Something isn't working issue-resolved 问题已解决

Comments

@Mq-b
Copy link
Owner

Mq-b commented Aug 3, 2024

start 成员函数:

void start(){
    for (std::size_t i = 0; i < num_threads_; ++i){
        pool_.emplace_back([this] {
            while (!stop_) {
                Task task;
                {
                    std::unique_lock<std::mutex> lc{ mutex_ };
                    if (tasks_.empty())
                        return;
                    cv_.wait(lc, [this] {return stop_ || !tasks_.empty(); });
                    task = std::move(tasks_.front());
                    tasks_.pop();
                }
                task();
            }
        });
    }
}

问题在于:

std::unique_lock<std::mutex> lc{ mutex_ };
if (tasks_.empty())
    return;

这样写可能导致线程开始执行,任务还没加入,队列为空,就直接退出了

ThreadPool pool{ 4 }; // 构造函数调用 start 成员函数,任务还未加入,直接退出
// ...
pool.submit(task); // 任务还未加入

有时你可以看到任务正确执行,有时不行。这个问题的原因很简单: pool_.emplace_back 只是构造线程对象,线程对象构造代表启动线程,但是线程何时执行,是系统调度的事情,也和硬件有所关系。

正常的做法我们应该是将判断移动到 cv_.wait 的下面,也就是:

std::unique_lock<std::mutex> lc{ mutex_ };
cv_.wait(lc, [this] {return stop_ || !tasks_.empty(); });
if (tasks_.empty())
    return;

join() 成员函数:

void join(){
    for (auto& thread : pool_) {
        if (thread.joinable()) {
            thread.join();
        }
    }
    pool_.clear();
}

但是一旦改成这样,我们的 join() 成员函数则可能出现阻塞,这是因为:

  1. 假设线程池有三个线程,添加了 2 个任务给线程池执行,线程 1 2 去执行完毕了,任务队列也就空了

  2. 然而线程 3 在调用 wait 函数的时候被阻塞了。

    cv_.wait(lc, [this] {return stop_ || !tasks_.empty(); });
  3. 我们调用 join() 成员函数,另外两个线程正常的退出,但是线程 3 会被一直阻塞在 wait 无法退出,因为条件始终无法满足,stop_ 只有在调用 stop() 成员函数后才会为 true,而 !tasks_.empty() 因为线程队列已经为空,所以始终为 false将一直阻塞

修改

放弃 join() 成员函数并修改给出的使用示例,

@Mq-b Mq-b added the bug Something isn't working label Aug 3, 2024
Mq-b added a commit that referenced this issue Aug 3, 2024
1. fix 线程池的实现中存在错误 #24
2. 修改示例的打印输出,增加线程 id
@Mq-b Mq-b closed this as completed Aug 3, 2024
@Mq-b Mq-b added resolved 此问题已解决 issue-resolved 问题已解决 and removed resolved 此问题已解决 labels Aug 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working issue-resolved 问题已解决
Projects
None yet
Development

No branches or pull requests

1 participant