线程控制
pthread库
与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的
要使用这些函数库,要通过引入头文<pthread.h>
链接这些线程函数库时要使用编译器命令的“-lpthread”选项
创建 :
main和new线程谁先运行?不确定
等待:
操作:
问题:
我们期望谁最后退出?main thread 进行等待擦屁股。join来保证main线程最后退出,join方法会阻塞主线程,直到调用的线程完成执行
如果不join的话,如果主线程退了main函数退了代表函数结束了则进程结束了,所有线程都会退;不join会造成类似僵尸进程的问题 ;
tid是什么样子的?
全面看待线程函数传参:我们可以传递任意类型,但你一定要能想得起来,也能传递类(class)对象的地址
但是我们不太推荐直接在main函数,因为我们的整形,字符串,class是自己定义的在main函数的栈上开辟的空间,所以我们刚刚传递进去的td,是新线程访问了主线程栈上的变量;我们不太推荐因为一方面它破坏了主线程的完整性和独立性,其次如果在来一个线程也访问主函数栈上的定义的临时变量,那么他们两个线程对数据是读取还好但是修改呢?会影响另一个线程;我们更推荐在堆上申请一段空间然后把堆申请的临时变量例如指针拷贝给线程,那么就相当于你在你的主线程里面创建了堆空间因为这个堆空间一旦申请出来了其他线程也能看到但必须得有地址,只要把地址传递给拷贝给线程,那么堆空间就相当于交给这个线程了,未来在想要第二个线程,你再重新new堆上的空间再交给新线程,这样大家人手一个堆空间,多线程就不会干扰了;
等待函数传参 :
#include <iostream> #include <unistd.h> #include<ctime> #include<string> #include<pthread.h> class thread{ public: std::string name; int a; }; void*threadrun(void*agv){ //std::string name=(char*)agv; //int a=*(int*)agv;//传进来a的地址再强转+解引用 thread *td=static_cast<thread*>(agv);//安全类型的强转,(thread*)agv int cnt=5; while(cnt){ std::cout<<td->name<<" run thread ,cnt: "<<cnt--<<std::endl; sleep(1); } delete td; return (void*)111; } std::string printfto(pthread_t &tid){ char buffer[64]; snprintf(buffer,sizeof(buffer),"0x%lx",tid);//%lx用于输出无符号长整数 return buffer; } int main(){ pthread_t tid;//每个线程都要有自己的线程id;是由pthread库提供的typedef unsigned long int pthread_t;无符号整数 //main和new线程谁先运行?不确定 //int a=100; thread *td=new thread(); td->name="thread 1"; td->a=100; int n=pthread_create(&tid,nullptr,threadrun,(void*)td);//创建,注意最后一个参数加void*,并且第四个传递的是地址 if(n!=0){//失败 std::cerr<<"create thread error"<<std::endl; return 1; } //见见tid std::string str=printfto(tid);//16进制打印 std::cout<<"tid: "<<str<<std::endl; std::cout<<"main thread join begin......"<<std::endl; //我们期望谁最后退出?main thread ,你如何保证呢? void*code=nullptr;//注意这里是一个指针,并且开辟了空间的 n=pthread_join(tid,&code);//main进行等待, join来保证main线程最后退出,join方法会阻塞主线程,直到调用的线程完成执行//如果不join的话,如果主线程退了main函数退了代表函数结束了则进程结束了,所有线程都会退;不join会造成类似僵尸进程的问题 if(n==0){ std::cout<<"main thread wait succes!new thread exit codeL "<<(uint64_t)code<<std::endl; } return 0; }
全面看待线程函数返回:
a.只考虑正确的返回,不考虑异常,因为异常了则整个进程都崩溃了包括主线程(任何一个线程崩掉了意味着这个进程崩了,那么意味着这个进程的父进程关心他的退出情况)b.我们可以传任意类型包括类对象
如何创建多线程?
错误创建:正确创建:
等待:
线程的终止:
新线程如何终止?
1. 函数return。只要新线程把自己的函数执行完了就代表新线程执行结束,我们可以在函数里调用各种函数和程序替换,但是只要return了就结束了
exit()退出:不能用来线程,他是专门用来进程退出的
2. pthread_exit:专门用来终止一个线程的
3. pthread_cancel:用于请求取消指定的线程,新线程的退出结果是-1
在 POSIX 线程(pthread)库中,PTHREAD_CANCELED 是一个特殊的常量,用于指示线程已被取消。
可不可以不join线程,让他执行完毕就退出呢?可以
主线程如何终止?
main函数结束,主线程结束表示整个进程都结束了,所以尽量保证主线程最后退出保证其他新线程执行完毕;
问题:
可不可以不join线程,让他执行完毕就退出呢?可以
pthread_detach:是 POSIX 线程(pthread)库中的一个函数,用于将线程的状态设置为“分离”(detached)。一旦线程被分离,它的资源会在线程结束时自动释放,这样就不需要其他线程调用 pthread_join 来回收它的资源。
错误检查:
1. 传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误。
2. pthreads函数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做)。而是将错误代码通过返回值返回
3. pthreads同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthreads函数的错误, 建议通过返回值业判定,因为读取返回值要比读取线程内的errno变量的开销更小
野指针:
野指针是指向一个未定义或不再有效内存区域(已经释放的即delete或不再有效的内存)的指针 ;
未初始化的指针:指针在声明后未被赋值,默认为随机值。
int* p; // 未初始化的指针
释放后将指针置为 nullptr:在释放内存后,立即将指针设置为 nullptr,防止继续访问已释放的内存。
delete p; // 释放内存 p = nullptr; // 防止成为悬空指针
int *p = nullptr; 将指针 p 初始化为 nullptr,这意味着此时 p 不指向任何有效的内存地址。此时再想用只能p=new int,*p=100;不能直接*p=100;
int *p = nullptr;// 初始化指针为 nullptr // 动态分配内存并赋值 p = new int; // 分配内存 *p = 42; // 给指针指向的内存赋值 // 释放内存 delete p; // 释放动态分配的内存 p = nullptr; // 防止成为野指针