线程控制
POSIX线程库
- 与线程有关的函数构成了⼀个完整的系列,绝⼤多数函数的名字都是以“pthread_”打头的
- 要使⽤这些函数库,要通过引入头文件<pthread.h>
- 链接这些线程函数库时要使⽤编译器命令的“-lpthread”选项 eg: g++ -o $@ $^ -lpthread
- 这个pthread库是linux自带的原生线程库 , C++11也有线程<thread>库,在Linux上本质就是封装了<pthread>库,
C++11 的
<thread>库
是跨平台的,在 Windows 上可能封装CreateThread
,在 macOS 上封装pthread
,在 Linux 上通常封装pthread
。
创建线程
功能:创建⼀个新的线程
原型:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *
(*start_routine)(void*), void *arg);
参数:
输出型参数thread:返回线程ID
attr:设置线程的属性,attr为NULL表⽰使⽤默认属性
start_routine:是个函数地址,线程启动后要执⾏的函数
arg:传给线程启动函数的参数
返回值:成功返回0;失败返回错误码(错误码一般大于0)
vfork已经基本被废除了
在linux内核看来 ,只有LWP ,所以向外只提供了clone一种系统调用, 通过封装clone, fork可以创建子进程 ,pthread_creat可以创建线程.
但是在Windows内核下,提供了创建进程的syscall 和创建线程的syscall (因为windos中确实单独实现了线程 和进程) linux下只有LWP
错误检查:
- 传统的⼀些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指⽰错误。
- pthreads函数出错时不会设置全局变量errno(⽽⼤部分其他POSIX函数会这样做)。⽽是将错误代码通过返回值返回
- pthreads同样也提供了线程内的errno变量,以⽀持其它使⽤errno的代码。对于pthreads函数的错误,建议通过返回值l来判定,因为读取返回值要⽐读取线程内的errno变量的开销更⼩
创建一个线程
#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<string>
void* routine (void* args)
{
//static_cast 是 C++ 中的 静态类型转换运算符,用于在 编译时 进行类型检查的显式转换。它比 C 风格的强制转换 (type)value 更安全
//使用方法:static_cast<目标类型>(表达式)
std::string name = static_cast<const char *>(args);// 将 args(某种类型)显式转换为 const char *,并用它构造一个 std::string 对象 name
while(true)
{
std::cout<<"我是子线程 我的名字是 "<<name<<std::endl;
sleep(1);
}
return 0;
}
int main()
{
pthread_t tid;
int n =pthread_create(&tid ,nullptr ,routine,(void*)"thread-1");
if(n!=0 )
{
std::cout<<"creat thread error"<<std::endl;
return 1;
}
while(true)
{
std::cout<<"我是main线程 "<<std::endl;
sleep(1);
}
return 0;
}
每个线程都有自己的ID ,在创建时就以第一个参数形式输出了,也可以使用函数pthread_self( ) 获取自己线程的ID
两个线程同时访问同一个公共资源有可能引发错误(像显示器打印,在不加保护的情况下,显示器也是公共资源)下面可以看到打印错乱,就是这个原因,就是我们以前讲的重入.
我们可以看到main线程和子线程的ID不同
总结:
- 新线程和main线程谁先运行 ,不确定
- 线程要瓜分进程的资源,包括时间片
- 不加保护的情况下 ,显示器文件就是公共资源
- 对于多线程代码 ,进程内的函数 ,被所有线程共享,只要这个函数不使用全局变量(显示器文件就是全局变量),就是安全的
- 全局变量能被此进程中的全部线程看到
- 一旦一个线程出现异常,进程就崩了(被信号杀死了),其它线程也就全崩了 例如:一个线程中使用了野指针 ,查页表时失败了,MMU报错 ,导致CPU触发软中断 ,OS发送信号杀死进程.
- 线程创建后也是要被等待和回收的(主线程像父进程一样一定是最后退出的)