day11-Thread
one loop per thread = 一个线程,只跑一个 EventLoop
muduo 的 Thread 类 = 专门用来创建这种 “带 loop 的线程”
Thread类是对线程的一个轻量级封装
Thread.h
1 |
1 | class Thread : noncopyable |
一个线程对象只能代表一个线程,不能复制
定义线程要执行的函数类型,无参数、无返回值的函数
私有成员变量
1 | private: |
- 这个
started_可以不要,started_的作用 = 记录是否调用过 start,智能指针自带这个功能。 numCreated_是一个全局的、线程安全的线程编号计数器static:所有Thread对象共用这一个变量,全局唯一- atomic
原子变量,多线程同时创建线程也不会计数错乱。
线程变量(thread_)为什么要用只能指针?
为什么用指针:线程不是构造时就创建,而是 start () 时才创建,用指针才能动态创建。
- Thread t(func); 此时还没有线程
- 调用 t.start(); 才真正创建线程
- 不用指针构造
Thread时就必须创建线程,控制不了时机。
为什么用只能指针不用裸指针:自动管理生命周期
为什么用
unique_ptr没用shared_ptr:std::thread 本身就不能拷贝,不能共享,用shared_ptr去包一个本来就不能共享的东西,逻辑上就多此一举。且unique_ptr没用引用计数,内存更小。(但这里用shared_ptr也不算错,只是语义不适合)
内部函数
1 | private: |
你创建线程时不传名字,它就自动帮你起一个。
成员函数
1 | //用「const 左值引用」绑定「临时对象」,既实现了参数的默认值,又兼顾了性能和安全性。 |
const std::string& name
线程名字
用
const &好处:- 不拷贝字符串
- 能接收临时字符串(如
"ThreadTest") - 安全、高效
= std::string()
- 默认参数
- 如果你不传名字,就自动用空字符串
- 然后类内部会调用
setDefaultName()自动生成Thread1、Thread2...
1 | void start(); |
启动线程
1 | void join(); |
阻塞等待线程跑完
1 | pid_t tid() const { return tid_; } |
- 返回 & 引用 = 不复制字符串
如果返回
std::string→ 会复制一份字符串
→ 浪费性能、浪费内存如果返回
const std::string&→ 直接返回类内部的那个字符串本身
→ 0 拷贝、0 开销、最快
- 加 const = 外面绝对改不了!
- 线程名字只能在内部设置
- 外面只能读,不能改
- 加
const就是强制保护
1 | static int numCreated() { return numCreated_; } |
查看全局创建了多少个线程
Thread.cc
1 |
头文件依赖
1 | std::atomic_int Thread::numCreated_{0}; |
静态变量初始化
1 | void Thread::setDefaultName() |
如果
name_是空 → 生成名字如果
name_不为空 → 保留你传的名字
1 | Thread::Thread(ThreadFunc func, const std::string& name) |
thread_不在这初始化,start()时才真正创建线程,初始化thread_。thread_是智能指针,会自动初始化为nullptr。
1 | Thread::~Thread() |
thread_:智能指针是否不为空,保证线程对象真的创建了thread_->joinable():线程是否join()过detach ()= 分离线程,不会阻塞==核心设计:==这里
detach()不用害怕生命周期问题(一旦启动完成,Thread 对象就可以随时销毁),是因为start()中fut.get()同步等待的设计,它用 等待 保证:访问 this 的阶段,this 一定存活之后就再也不访问 this。
1 | void Thread::join() |
joinable()替代原来joined_标记。
1 | // 启动线程,并保证 tid_ 赋值完成才返回 |
原来的代码用 信号量 sem(C 语言接口,不是 C++ 标准)
1 | sem_t sem; |
现在用 std::promise + std::future
1 | sem_init = 创建 promise |
- 创建同步工具
1 | std::promise<pid_t> prom; |
- promise:子线程用,用来发数据、发通知
- future:主线程用,用来等数据、等通知
- 创建线程
1 | thread_ = std::make_unique<std::thread>([this, &prom]() { |
- 启动新线程
- 捕获 prom,让子线程能发通知给主线程
- 子线程获取 TID 并发消息
1 | pid_t sys_tid = CurrentThread::tid(); |
- 获取系统 TID
- 把值传给主线程 + 唤醒主线程
- 主线程阻塞等待
1 | tid_ = fut.get(); |
- 等子线程发消息
- 拿到 TID,赋值给 tid_
- 然后
start()才返回
源码地址
Thread.h:https://gitee.com/lpzdinghai/lpzmuduo/blob/master/Thread,h
Thread.cc:https://gitee.com/lpzdinghai/lpzmuduo/blob/master/Thread.cc