当前位置: 首页 > article >正文

std::thread()函数的第一个参数的使用细节

今天突然想着写一遍生产者消费者模型,结果写代码时遇到了一连串问题,就把他记录下来供后面参考吧。
在下面的代码中,当我希望在构造函数中通过thread p1(sample::consumer , this , 1);来创建一个消费者线程时,发现该语句报错Reference to non-static member function must be called,后面发现这是提示传入的非静态成员函数要能够调用,把这句改成thread p1(&sample::consumer , this , 1);就不会报错了,因此我觉得非常奇怪,sample::consumer&sample::consumer不是都能够代表函数指针吗,为什么第一种写法会出错呢?
后面查资料知道thread()函数的第一个参数必须要是可调用对象,而如果传入的是函数指针的话,函数指针是分为普通函数指针(全局函数、静态成员函数等,总而言之就是不需要绑定到某个实例上就可以独立运行的函数)成员函数(也就是需要与某个实例进行绑定才能运行的函数),而对于thread()bind()来说,如果传入的形式是thread p1(sample::consumer , this , 1);,那么sample::consumer实际上会被当做是普通函数指针,进而将后面两个参数作为sample::consumer函数的参数传入,结果就是报错(因为没有产生一个可调用的对象嘛),而如果传入的形式是thread p1(&sample::consumer , this , 1);,那么sample::consumer实际上会被当做是成员函数指针(这个规则就是C++规范定义的),而成员函数指针的第一个参数默认就是指向执行对象的指针,因此整个式子也就能够被顺利生成一个可调用对象。
因此我之前疑问的原因就是因为对C++规范中对普通函数和成员函数的区分不熟悉,如果要把成员函数以及其要绑定的对象指针进行绑定,那么传入的函数名前面一定要加&,以告诉编译器这是一个成员函数。,而如果对于普通的取函数指针,那么函数名前面加不加&,都能得到正确的函数地址。
另外还有个小细节就是thread p1(&sample::consumer , this , 1);如果改成thread p1(&consumer , this , 1);,也会报错,这其实还是可以归结为编译器要把普通函数和成员函数区别开,也就是对于成员函数,必须要把它的全名,也就是包括类名这种命名空间也要写全,这主要是为了将普通函数区分开,比如在下面代码中对于静态成员变量hello()函数,在类的构造函数中就只需要通过thread p2(hello);来将其传入。

#include <bits/stdc++.h>
using namespace std;

class sample {
private:
    int task = 1;
    mutex mtx;
    condition_variable cv;
    queue<int> tasks;
    int queue_max = 8;

    void prodocutor(int id) {
        unique_lock<mutex> lk(mtx);
        while(tasks.size() >= queue_max) {
            cv.wait(lk,[&] {return tasks.size() < queue_max;});
            
        }
        int tmp = task++;
        tasks.push(tmp);
        lk.unlock();
        cv.notify_all(); // 生产者添加任务后唤醒消费者
        cout << "prodocutor " << id << "produce task:" << tmp << endl;
        this_thread::sleep_for(chrono::seconds(1));
        lk.lock();
    }

    void consumer(int id) {
        unique_lock<mutex> lk(mtx);
        while(tasks.size() == 0 ) {
            cv.wait(lk, [&] {return tasks.size() > 0 ;});
        }
        int tmp = tasks.front();
        tasks.pop();
        lk.unlock();
        cv.notify_all(); // 消费者消耗任务后唤醒生产者
        cout << "consumer  " << id << "consume task:" << tmp << endl;
//        this_thread::sleep_for(chrono::seconds(1));
        lk.lock();
    }
    static void hello() {

    }


public:
    sample() {
         thread p1(sample::consumer , this , 1); // 错误写法
         //thread p1(&sample::consumer , this , 1); // 正确写法
         thread p2(hello); // 对于静态成员函数就不需要加命名空间
     };


};



int main() {

}

PS:用GPT总结了下*类的成员变量和成员函数的存储位置,简洁来讲就是也就是类当中有成员变量和成员函数,对于静态成员变量都是存储在全局区的,而非静态成员变量根据它是局部变量还是动态申请的内存来决定是在栈区还是在堆区,而函数都是在代码区的


http://www.kler.cn/a/421108.html

相关文章:

  • Meta-Llama-3-8B-Instruct 模型的混合精度训练显存需求:AdamW优化器(中英双语)
  • 安装MySQL 5.7 亲测有效
  • jvm-46-jvm Thread Dump 线程的信息获取+可视分析化工具 FastThread
  • vue3+element-plus多选框全选与单选
  • 我们项目要升级到flutter架构的几点原因
  • Python学习38天
  • FreeSWITCH mod_conference 的按键会控
  • 【C++】智能指针的使用和原理
  • 总结拓展十七:特殊采购业务——委外业务
  • 数据结构——有序二叉树的删除
  • 【Tr0ll2靶场渗透】
  • 帮我写一篇关于AI搜索网页上编写的文章是否存在版权问题的文章, 字数在 3000 字左右。文心一言提问, 记录后用.
  • Erlang数据库:Mnesia(一) —— 数据库查询
  • Linux——基础命令(3)
  • 叉车智能防撞系统选购攻略:多维度考量,确保安全作业
  • 中国移动量子云平台:算力并网590量子比特!
  • 红外传感器HW—201
  • Google Adx账号受限停用:风控何时结束?
  • 华为项目管理之道
  • 蓝桥杯每日一题-图书排序
  • 浅谈Java库之‌Apache Commons Math
  • 基于单片机的智能窗帘控制系统的设计与实现
  • 【Spring】注解开发
  • 基于SSM的停车场管理系统
  • Flink历史服务器-History Server
  • MATLAB提供的窗函数