为啥我的线程池有时会抛出 `std::bad_function_call` 或 `double free or corruption (!prev)`

Posted

技术标签:

【中文标题】为啥我的线程池有时会抛出 `std::bad_function_call` 或 `double free or corruption (!prev)`【英文标题】:Why does my thread pool sometimes throw `std::bad_function_call` or `double free or corruption (!prev)`为什么我的线程池有时会抛出 `std::bad_function_call` 或 `double free or corruption (!prev)` 【发布时间】:2020-08-14 19:37:25 【问题描述】:

大约 50% 的时间,我的线程池的测试不会抛出任何异常并且似乎按预期工作。然而,另外 50% 的时间它会抛出 std::bad_function_calldouble free or corruption (!prev)。我做错了什么?

#include <thread>
#include <iostream>
#include <atomic>
#include <any>
#include <stack>
#include <mutex>
#include <algorithm>

class reusable_thread 
    std::thread thread;
    std::atomic_bool kill = false;
    std::stack<std::function<void(void)>> function_stack;
    std::stack<std::function<void(void)>> pending_function_stack;
    std::mutex stack_mutex;
    std::atomic_size_t num_jobs = 0;

    /** Seperate containers for functions and pending_function_stack, so that add_job does not have to be locking **/
    inline void transfer_functions() 
        std::lock_guard lock(stack_mutex);
        while(pending_function_stack.size() != 0) 
            function_stack.push(pending_function_stack.top());
            pending_function_stack.pop();
        
    

public:

    /** So the least busy in a container can be found with std::min_element **/
    bool operator < (const reusable_thread& other) const  return this->num_jobs < other.num_jobs; 

    /** Start the thread: in loop transfer from pending_function_stack to function_stack, run all jobs, spin if no jobs are waiting. **/
    reusable_thread() 
        thread = std::thread([this]()
            while(!kill) 
                transfer_functions();
                if(function_stack.size() != 0) 
                    function_stack.top()();
                    function_stack.pop();
                    num_jobs--;
                
            
        );
    

    /** Transfer any last pending functions over, wait for them all to complete, then join the main thread **/
    ~reusable_thread() 
        transfer_functions();
        while(function_stack.size() != 0) 
        kill = true;
        thread.join();
    

    /** Add a job. Non locking**/
    void add_job(const std::function<void(void)>& f) 
        pending_function_stack.push(f);
        num_jobs++;
    

;

template<size_t N>
class thread_pool 
    std::array<reusable_thread, N> threads;
public:
    void add_job(const std::function<void(void)>& f) 
        auto&& least_busy = std::min_element(threads.begin(), threads.end());
        least_busy->add_job(f);
    
;

int main() 
    thread_pool<6> tp;
    for(auto i = 0; i < 1000; i++) 
        tp.add_job([]()std::cout << "Hello World" << std::endl; );
    
    std::cout << "All jobs added" << std::endl;

【问题讨论】:

你用的是什么编译器?在 killlock 的实例化时,g++ 9.3.0 出现错误。 @StefanScheller g++ 9.0.1,在 Linux 上。我也在启用 c++ 17 进行编译。 谢谢!现在我可以重现它了。 【参考方案1】:

add_jobtransfer_functions 之间存在竞争条件,因为一个在没有锁的情况下添加到 pending_function_stack,另一个正在检查它

【讨论】:

【参考方案2】:

您的 reusable_thread 析构函数调用transfer_functions,它在持有互斥锁的同时将函数推送到您的function_stack,但您线程中的主循环使用来自您的function_stack的函数获取互斥锁。因此,您在 function_stack 上存在数据竞争。

【讨论】:

以上是关于为啥我的线程池有时会抛出 `std::bad_function_call` 或 `double free or corruption (!prev)`的主要内容,如果未能解决你的问题,请参考以下文章

为啥有时会抛出 FileNotFoundException

为啥 PasteSpecial 方法有时会抛出错误 1004?

为啥即使使用了调用,组合框也会抛出异常

为啥从主线程调用时,`std::promise::set_value` 会抛出错误?

Handler的工作原理。为啥在子线程中使用Handler会抛出异常

在asp.net核心控制器中,为啥ExecutionContext.SuppressFlow()会抛出“AsyncFlowControl对象必须在创建它的线程上使用”。