目录
1.线程创建
int pthread_create(pthread_t *thread , const pthread *attr , void *(*start_routine)(void *) , void *arg);
头文件:
pthread.h
参数:
thread:获取线程标识符(地址),本质上就是线程独有空间的首地址
attr:线程的属性信息,一般填写NULL,采用默认的线程属性
start_routine:函数指针,线程执行的入口函数(线程执行起来的时候,从该函数开始运行,切记:不是从main函数开始运行)
arg:给线程入口函数传递参数
返回值:
成功返回0,失败返回值<0
代码举例:
1 #include<stdio.h>
2 #include<pthread.h>
3 #include<unistd.h>
4 void* mythread_start(void* arg){
5 while(1){
6 printf("i am my_thread_start\n");
7 sleep(1);
8 }
9 return NULL;
10 }
11 int main(){
12 pthread_t tid;
13 int ret=pthread_create(&tid,NULL,mythread_start,NULL);
14 if(ret<0){
15 perror("pthread_create\n");
16 return 0;
17 }
18 printf("success...\n");
19 return 0;
20 }
运行结果如下:
说明创建成功了,但是为什么工作线程没有继续运行呢
是因为主线程输出success之后就推出了,工作线程自然无法工作。
只要把主线程写进循环不让他那么早的退出,就能够观察到工作线程的工作状况。
1 #include<stdio.h>
2 #include<pthread.h>
3 #include<unistd.h>
4 void* mythread_start(void* arg){
5 while(1){
6 printf("i am my_thread_start\n");
7 sleep(1);
8 }
9 return NULL;
10 }
11 int main(){
12 pthread_t tid;
13 int ret=pthread_create(&tid,NULL,mythread_start,NULL);
14 if(ret<0){
15 perror("pthread_create\n");
16 return 0;
17 }
18 while(1){
19 printf("success...\n");
20 sleep(1);
21 }
22 return 0;
23 }
运行结果如下:
分析一下线程的堆栈信息:
结论:
(1)当创建完毕线程之后,主线程和工作线程都是独立被调度的。引申含义是:没有办法确定到底是先调用主线程还是工作线程,由操作系统调度决定的。
(2)如果创建工作线程完毕之后,但是,进程退出了,有可能看不到工作线程运行的结果。原因就是:没有了进程,就不肯拥有更改创建出来的工作线程。
(3)如果想看到工作线程的运行结果,则先保证进程不退出。如果想让工作线程不退出,则线程不能执行线程入口函数完毕。
(4)pstack查看线程信息。
是否可以创建多个线程(多线程)?
答案是肯定的,代码编写也很简单:
1 #include<stdio.h>
2 #include<pthread.h>
3 #include<unistd.h>
4 void* mythread_start(void* arg){
5 while(1){
6 printf("i am my_thread_start\n");
7 sleep(1);
8 }
9 return NULL;
10 }
11 int main(){
12 pthread_t tid;
13 int ret=pthread_create(&tid,NULL,mythread_start,NULL);
14 if(ret<0){
15 perror("pthread_create\n");
16 return 0;
17 }
18 ret=pthread_create(&tid,NULL,mythread_start,NULL);
19 if(ret<0){
20 perror("pthread_create\n");
21 return 0;
22 }
23 while(1){
24 printf("success...\n");
25 sleep(1);
26 }
运行结果如下:
正儿八经的确实创建出来了两个工作线程。,这两个线程执行的入口函数是同一个。
这两个线程看到的mythread_start栈帧是不是同一个呢?
答案肯定是不是的,利用工厂与流水线的关系也可以看出,不同的流水线完全可以执行不同的工作,如果所有流水线都是同一个的镜像,这样就失去了多线程的意义。
咱们再用for循环来批量生产工作线程:
1 #include<stdio.h>
2 #include<pthread.h>
3 #include<unistd.h>
4 void* mythread_start(void* arg){
5 int* i =(int*)arg ;
6 while(1){
7 printf("i am workthread... %d\n",*i);
8 sleep(1);
9 }
10 return NULL;
11 }
12 int main(){
13 int i;
14 for(i=0;i<4;i++){
15 pthread_t tid;
16 int ret=pthread_create(&tid,NULL,mythread_start,&i);
17 if(ret<0){
18 perror("pthread_create\n");
19 return 0;
20 }
21 }
22 while(1){
23 printf("success...\n");
24 sleep(1);
25 }
26 return 0;
27 }
观察一下运行结果:
很乱啊,为什么会出现这种情况;
换句话说为什么再之后的工作线程当中打印的i都是4呢。
因为函数中传递的入口空间一直都是i的地址,而i只是主函数中的一个循环变量,打印的始终都是i这个临时变量的地址空间,当i变成4之后,四个工作线程打印的自然都是4了。
这样运用循环来批量制造工作线程有风险码?
当然有,表现是多个线程在访问的i的空间是非法访问,因为i是临时变量,除了for循环的作用域之后就被销毁了。
结论:线程入口函数的参数不要传递临时变量,临时变量出了作用域之后就会被销毁。有可能线程在非法访问空间。
如何避免4444这种情况:
(1)各个线程直接打印自己的线程标识符pthread_self;
(2)给线程分别传递不同的堆上空间。
总结:
(1)线程入口函数传递参数的时候,传递堆栈空间;
(2)释放堆区空间的时候,让线程自己进行释放。
2.线程终止
void pthread_exit(void *retval);
参数:
retval:线程退出时,传递给等待线程的退出信息。
作用:
谁调用就由谁退出
int pthread_cancel(pthread_t thread);
参数:
thread:被终止的线程的标识符
作用:退出某个线程
3.线程等待
线程被创建出来的默认属性是joinable属性,退出的时候,依赖其他线程来回收资源(主要是退出线程使用到的共享区当中的空间)。
int pthread_join(pthread_t thread , void **retval);
参数:
thread:线程的标识符
retval:退出线程的退出信息
第一种:线程入口函数代码执行完毕,线程退出的,就是入口函数的返回值;
第二种:pthread_exit退出的,就是pthread_exit的参数;
第三组:pthread_cancel退出的,就是一个宏:OTHREAD_CANCELED。
4.线程分离
设置线程的分离属性,一旦线程设置了分离属性,则线程退出的时候,不需要任何人回收资源,操作系统可以自行回收。
int pthread_detach(pthread_t thread);
参数:
thread:设置线程分离的线程标识符。