线程可以通过ipc通信吗_教育理论基础知识

线程可以通过ipc通信吗_教育理论基础知识IPC——线程基础理论

大家好,又见面了,我是你们的朋友全栈君。

推荐阅读:加州大学圣迭戈分校Thread讲义

Process – (Kernel) Thread (Lightweight processes – LWP)

 

进程缺点

为了让每个进程有一个安全的独立进程空间,OS使用了虚拟内存机制,通过虚拟内存机制,能够让每一个进程都有完全独立的进程空间。这种独立的进程空间最大的优点就是,可以很好地保证每一个进程的安全,不被其它进程所攻击或者干扰,但是突出的优点往往又会导致另外的缺点。拥有独立进程空间的进程有两大明显的缺点:

①进程间切换的计算机资源开销很大,切换效率非常低

OS是通过虚拟内存机制来实现进程空间独立的,进程在并发运行时需要相互间的切换,切换时必然需要涉及虚拟内存机制的控制,但是虚拟内存机制比较复杂,所以在进行进程间切换时,会耗费高昂的cpu、缓存(cache)、内存等计算机资源,也非常耗费切换时间。

②进程间数据共享的开销也很大

当程序涉及多进程时,往往会涉及到进程间的通信,但是由于进程空间的独立性,OS提供了各种各样的通信机制,这些通信机制共同原理就是,通过OS来转发进程间的数据,但是调用OS提供的这些通信机制的函数时,这些OS函数的运行也是需要消耗相当cpu、内存等计算机资源的,同时也很耗费时间。因此,对于我们有OS的计算机来说,虽然进程是必不可少的,但是进程确又不能太多,进程太多会导致计算机资源被剧烈消耗,此时你会发现你的计算机非常的卡。

早期应用进程主要目的

①创建子进程,执行新程序

②创建子进程得到多进程,通过多进程并发实现多线任务

例如:同时阻塞的读鼠标和键盘时,如果单线的话会想互影响,需要两线任务来实现。读写管道时,读操作是阻塞的,为了避免读操作影响写操作,也需要两线任务同时进行。

对于第一种目的,执行新程序时必须创建子进程,这个无法逃避的。但是对于第二种目的来说,如果使用多进程来实现就存在巨大的问题,因为几乎所有的程序都涉及多线任务的操作,而且好些程序往往都是十几个任务以上,如果此时使用多进程来实现多线任务的,这就大致大量进程的产生。比如计算机运行了100个程序,假设每个程序平均10多个任务,如果全部采用多进程来实现,计算机最终要运行的进程就多达上1000个。所以在早期使用多进程来实现程序的多线任务时,往往导致计算机进程数量暴增,而进程切换和进程间通信的计算机资源开销又很大,因此往往导致计算机非常卡顿,程序的运行效率非常低。

引入线程

线程弥补了进程在多线并发上的不足。首先我们需要明白,线程与进程一样,线程和进程会被OS统一调度,所以所有的线程和进程都是一起并发运行的,如果线程不是并发的,是不可能实现程序的多线任务的。有了线程以后,凡是程序涉及到多线任务时,都使用多线程来实现,使用多线程来实现时,线程间的切换和数据通信的开销非常低,正因为开销非常低,因此线程还有另一个名称,叫“轻量级的进程”。

注意:斜体字部分,在多核处理器上,进程是可以并行的。但是线程还真不一定,线程的并行需要内核的支持才可以,单凭线程库可没这么大的本事。

轻量级进程,这个叫法是存疑的。需要强调一点的是英语中Thread和LWP指的不是一个东西,这种叫法估计是中文翻译过程中的粗心所致,希看到这段话的人能注意区分。下面是 加州大学圣迭戈分校的讲义

线程可以通过ipc通信吗_教育理论基础知识

线程间数据通信开销很低

线程的本质就是函数,函数间通信(数据共享)有两种方式:

①具有相互调用关系函数来说

使用函数传参来通信。

②对于没有调用关系的函数来说

使用全局变量来通信。A函数 ————> 全局变量 ————> B函数

所以说全局变量的作用是什么?就是用来实现无调用关系的函数间通信的。

进程中所有的线程函数除了相互并发运行外,没有调用关系,所以线程函数间想要数据共享的话,就使用全局变量来通信。

从这里可以看出,进程内部的线程间进行数据共享非常容易,使用全局变量即可,根本不需要调用什么OS提供的通信机制,所以线程间通信的开销自然就非常的低。

线程之间切换开销很低

使用多线程来实现多线任务时,由于线程本质上它只是程序(进程)的一个函数,只不过线程函数与普通函数的区别是,普通函数是单线的运行关系,而线程函数被注册为线程后,是多线并发运行。对于普通函数来说,只有当相互调动时才会涉及函数间的切换,但是对于线程函数来说,只要运行的时间片到了就会切换,但是不管是那种函数间的切换,进程自己函数的切换只是进程内部的事情,不涉及进程间切换,就省去了进程间切换的巨大开销。进程内部的线程,他们之间的相互切换是由线程库负责的。

如果是不同进程的线程之间需要切换的话,还是会涉及到进程间的切换了,但是不管怎们说,线程的出现,至少为程序内部多线任务之间的切换,省去了大笔的进程切换所导致“资源开销”。

线程的切换其实就是函数间的切换,函数切换当然也需要开销,但是这些开销相比进程间切换的开销来说,已经非常小了。

可否只要线程不要进程

不可以。

线程是不可能完全替代掉进程的,只有在多线任务时会替代进程,但是运行新程序的时,我们还是必须创建子进程。线程的本质是函数,函数运行需要内存空间,这个内存空间怎么来,事实上线程运行的内存空间就是进程的内存空间,因此线程运行时必须依赖于进程的存在,如果没有进程所提供的内存空间这个资源,线程根本无法运行。换句话说,线程作为函数,只是进程的一个部分而已,线程是不可能脱离进程而独立存在。所以同一个进程中的所有线程,都是运行在相同的进程空间中的,换句话说同一个进程中所有线程共相同的进程空间。

线程可以通过ipc通信吗_教育理论基础知识

参考:剖析可执行文件ELF组成 可执行文件内存布局

线程自己独立的属性

进程中的所有线程会共享进程提供资源(全局变量、工作目录、打开的文件描述符、子函数等等),但是每个线程作为一个单独的执行体,也有属于自己的独立的东西。

每个线程拥有自己独立的线程ID(TID)

每个线程有独立的切换状态

1)在切换时,当前线程被中断的那条指令的地址

2)线程切换时相关的运行状态

当线程切换时,必须保存以上信息,以便切换回来后还原现场,从中断处继续运行。

有自己独立的函数栈

其实每一个函数都有自己的函数栈,所有的函数栈都开辟于进程空间的进程栈。函数栈的作用就是用来保存函数局部变量的,既然所有的线程函数都有自己的独立的函数栈,自然就有自己独立的局部变量。线程函数的函数栈,我们往往也称为线程栈。

自己独立的错误号

线程函数的错误号是独立的,所以线程函数出错时,错误号并不是通过设置errno实现的,而是直接将错误号返回。

每一个线程有自己独立的信号屏蔽字和未决信号集  参考:IPC——信号

每个线程有自己独立的tack_struct结构体

进程在运行的过程中,OS会为每个进程开辟一个task_struct结构体变量用于存放进程所涉及到的各种管理信息,同样的为了管理线程,也会为线程开辟一个task_struct变量,只不过适用于存放线程的管理信息的。

API

实现多进程的时候有进程控制,进程控制涉及到的函数有fork、exec、wait、exit等函数,实现多线程的时候同样有线程控制函数,这些线程控制函数有:pthread_create、pthread_join、 pthread_detach、pthread_cancel、pthread_exit等。

进程控制的fork、exec等函数都是由os系统提供,但是线程控制函数却不是有os提供。原本线程函数也可以完全由OS来实现,但是后来为了不给OS增加负担,同时也为了提高线程的灵活性,后来的线程就不再由OS提供,而是由单独的线程库来提供,不过线程库在实现时,也是调用了相应的系统API的,也就是说线程的核心实现也是离不开OS支持的。

如果man手册查不到线程库函数,可以在线安装

//Ubuntu
sudo apt-get install glibc-doc :安装线程库
sudo apt-get install manpages-posix-dev:安装线程库的函数手册

//CentOS
yum -y install man-pages 

pthread_create

原型

#include <pthread.h>           
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

功能

把void *(*start_routine) (void *)函数注册为一个线程函数,该函数一旦注册成功,这个函数就以次线程的方式开始并发运行起来,如果不注册的话,这个函数就是一个普通函数。凡是使用pthread_create注册的线程,都是次线程,次线程会和主线程一起并发运行。

谁是主线程?

main函数的那条线就是主线程,如果一个次线程都没有创建的话,整个进程就只有一个主线程,这个主线程也是整个进程。

参数

thread:存放线程的TID。

attr:用于设置线程属性,设置线程属性的目的是为了实现某些特殊功能,如果设置为NULL,表示不设置特有的属性,使用线程默认属性所提供的功能即可。正常情况下,线程默认属性所提供的功能就已经够用了,所以这个参数我们都是设置为NULL。

start_routine:要注册为线程的函数地址

函数类型为void *(*) (void *),pthread_create它会把这个函数注册为线程,如果不注册,线程函数就是一个普通的函数。

线程函数需要我们自己定义,比如:

void *pth_fun(void *pth_arg)
{
    ...//线程要做的事情
}

pth_fun和pth_arg的命名由自己决定。

arg:传递给线程函数的参数,这个参数会传递给pth_arg,如果参数很多的话,我们做成一个结构体,然后把结构体变量的地址传过去。如果你不想传递参数的话,你可以设置为NULL。

返回值

成功返回0,失败返回非零错误号。

代码演示

创建两个次线程,主线程像文件中写hello world,两个次线程向同一个文件中写HELLO WORLD。

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <pthread.h>
 5 #include <string.h>
 6 #include <errno.h>
 7 #include <sys/types.h>
 8 #include <sys/stat.h>
 9 #include <fcntl.h>
10 #include <signal.h>
11 
12 #define SECON_PTH_NUMS     2  //次线程数量
13 
14 /* 传递给线程的参数 */
15 typedef struct pthread_arg 
16 {
17     pthread_t tid;//存放线程tid
18     int pthno;//我自己定义的编号
19     int fd;//文件描述符
20 }ptharg;
21 
22 struct gloable_va
23 {
24     ptharg pth_arg[SECON_PTH_NUMS];//结构体数组,每个元素会被当做参数传递给对应的次线程
25     int pth_exit_flg[SECON_PTH_NUMS];//每个元素存放对应编号线程的退出状态
26     pthread_attr_t attr;//存放线程新属性
27 }glbva;
28 
29 void print_err(char *str, int line, int err_no)
30 {
31     printf("%d, %s:%s", line, str, strerror(err_no));
32     exit(-1);
33 }
34 
35 void *pth_fun(void *pth_arg)
36 {    
37     int fd = ((ptharg *)pth_arg)->fd;
38     int pthno = ((ptharg *)pth_arg)->pthno;
39     pthread_t tid = ((ptharg *)pth_arg)->tid;    
40     while(1)
41     {
42         write(fd, "HELLO ", 6);
43         write(fd, "WORLD\n", 6);
44     }
45 
46     return NULL;
47 }
48 
49 void signal_fun(int signo)
50 {
51     if(SIGINT == signo)
52     {
53         exit(0);
54     }
55 }
56 
57 int main(void)
58 {
59     int fd = 0;
60     int i = 0;
61     int ret = 0;
62         
63     //打开文件,供线程操作,所有的线程(函数)可以共享打开的文件描述符
64     fd = open("./file", O_RDWR|O_CREAT|O_TRUNC, 0664);
65     if(fd == -1) print_err("open ./file fail", __LINE__, errno);
66 
67     /* 通过循环创建两个次线程 */
68     for(i=0; i<SECON_PTH_NUMS; i++)
69     {
70         glbva.pth_arg[i].fd = fd;//保存文件描述符
71         glbva.pth_arg[i].pthno = i; //我自己给的线程编号
72                                    //创建好次线程后,讲次线程分离
73         ret = pthread_create(&glbva.pth_arg[i].tid, &glbva.attr, pth_fun, (void *)&glbva.pth_arg[i]);    
74         if(ret != 0) print_err("pthread_create fail", __LINE__, ret);
75     }
76 
77     signal(SIGINT, signal_fun);
78     
79     while(1)
80     {
81         write(fd, "hello ", 6);
82         write(fd, "world\n", 6);
83     }        
84     
85     return 0;
86 }

View Code

file

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

 1 hello HELLO world
 2 WORLD
 3 hello HELLO world
 4 WORLD
 5 hello HELLO world
 6 WORLD
 7 hello HELLO world
 8 WORLD
 9 hello HELLO world
10 WORLD
11 hello HELLO world
12 WORLD
13 hello HELLO world
14 WORLD
15 hello HELLO world
16 WORLD
17 hello HELLO world
18 WORLD
19 hello HELLO HELLO world
20 WORLD
21 WORLD
22 hello HELLO HELLO world
23 WORLD
24 WORLD
25 hello HELLO HELLO world
26 WORLD
27 WORLD
28 hello HELLO HELLO world
29 WORLD
30 WORLD
31 hello HELLO HELLO world

View Code

pthread_cancel

原型

#include <pthread.h>                 
int pthread_cancel(pthread_t thread); 

功能

当次线程是死循环时,可以调动这个函数主动取消次线程。

参数

thread:要取消线程的TID

返回值

成功返回0,失败返回非零错误号。

代码演示

alarm定时2秒触发SIGALRM,捕获SIGALRM信号取消次线程。2s后次线程不在打印“#######”,只有主线程向file文件写数据

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <pthread.h>
 5 #include <string.h>
 6 #include <errno.h>
 7 #include <sys/types.h>
 8 #include <sys/stat.h>
 9 #include <fcntl.h>
10 #include <signal.h>
11 
12 #define SECON_PTH_NUMS     2  //次线程数量
13 
14 /* 传递给线程的参数 */
15 typedef struct pthread_arg 
16 {
17     pthread_t tid;//存放线程tid
18     int pthno;//我自己定义的编号
19     int fd;//文件描述符
20 }ptharg;
21 
22 struct gloable_va
23 {
24     ptharg pth_arg[SECON_PTH_NUMS];//结构体数组,每个元素会被当做参数传递给对应的次线程
25     int pth_exit_flg[SECON_PTH_NUMS];//每个元素存放对应编号线程的退出状态
26     pthread_attr_t attr;//存放线程新属性
27 }glbva;
28 
29 void print_err(char *str, int line, int err_no)
30 {
31     printf("%d, %s:%s", line, str, strerror(err_no));
32     exit(-1);
33 }
34 
35 void *pth_fun(void *pth_arg)
36 {    
37     int fd = ((ptharg *)pth_arg)->fd;
38     int pthno = ((ptharg *)pth_arg)->pthno;
39     pthread_t tid = ((ptharg *)pth_arg)->tid;    
40     while(1)
41     {
42         write(fd, "HELLO ", 6);
43         write(fd, "WORLD\n", 6);
44         printf("#########\n");
45     }
46 
47     return NULL;
48 }
49 
50 void signal_fun(int signo)
51 {
52     if(SIGALRM == signo)
53     {
54         int i = 0;
55         for(i=0; i<SECON_PTH_NUMS; i++)
56         {
57             pthread_cancel(glbva.pth_arg[i].tid);//取消次线程
58         }
59     }
60     else if(SIGINT == signo)
61     {
62         exit(0);
63     }
64 }
65 
66 int main(void)
67 {
68     int fd = 0;
69     int i = 0;
70     int ret = 0;
71         
72     //打开文件,供线程操作,所有的线程(函数)可以共享打开的文件描述符
73     fd = open("./file", O_RDWR|O_CREAT|O_TRUNC, 0664);
74     if(fd == -1) print_err("open ./file fail", __LINE__, errno);
75 
76     /* 通过循环创建两个次线程 */
77     for(i=0; i<SECON_PTH_NUMS; i++)
78     {
79         glbva.pth_arg[i].fd = fd;//保存文件描述符
80         glbva.pth_arg[i].pthno = i; //我自己给的线程编号
81                                    //创建好次线程后,讲次线程分离
82         ret = pthread_create(&glbva.pth_arg[i].tid, &glbva.attr, pth_fun, (void *)&glbva.pth_arg[i]);    
83         if(ret != 0) print_err("pthread_create fail", __LINE__, ret);
84     }
85 
86     signal(SIGINT, signal_fun);
87 
88     /* 定时2秒,时间到后取消次线程 */
89     signal(SIGALRM, signal_fun);
90     alarm(2);    
91 
92     while(1)
93     {
94         write(fd, "hello ", 6);
95         write(fd, "world\n", 6);
96     }        
97     
98     return 0;
99 }

View Code

pthread_cancel取消线程的方式比较粗暴,上面代码,如果次线程在执行第42行的时候,SIGALRM来到,他会取消次线程的执行,直接返回。也就是说43行(含43)往后代码都不会执行。如果第42行执行过程中,SIGALRM来到,他会等42行执行完,再取消次线程。

改良代码,增加线程退出状态标记

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <pthread.h>
  5 #include <string.h>
  6 #include <errno.h>
  7 #include <sys/types.h>
  8 #include <sys/stat.h>
  9 #include <fcntl.h>
 10 #include <signal.h>
 11 
 12 #define SECON_PTH_NUMS     2  //次线程数量
 13 #define PTHEXIT     -1    
 14 
 15 /* 传递给线程的参数 */
 16 typedef struct pthread_arg 
 17 {
 18     pthread_t tid;//存放线程tid
 19     int pthno;//我自己定义的编号
 20     int fd;//文件描述符
 21 }ptharg;
 22 
 23 struct gloable_va
 24 {
 25     ptharg pth_arg[SECON_PTH_NUMS];//结构体数组,每个元素会被当做参数传递给对应的次线程
 26     int pth_exit_flg[SECON_PTH_NUMS];//每个元素存放对应编号线程的退出状态
 27     pthread_attr_t attr;//存放线程新属性
 28 }glbva;
 29 
 30 void print_err(char *str, int line, int err_no)
 31 {
 32     printf("%d, %s:%s", line, str, strerror(err_no));
 33     exit(-1);
 34 }
 35 
 36 void *pth_fun(void *pth_arg)
 37 {    
 38     int fd = ((ptharg *)pth_arg)->fd;
 39     int pthno = ((ptharg *)pth_arg)->pthno;
 40     pthread_t tid = ((ptharg *)pth_arg)->tid;    
 41     while(1)
 42     {
 43         write(fd, "HELLO ", 6);
 44         write(fd, "WORLD\n", 6);
 45         //检测退出状态
 46         if(glbva.pth_exit_flg[pthno] == PTHEXIT) break;
 47     }
 48 
 49     return NULL;
 50 }
 51 
 52 void signal_fun(int signo)
 53 {
 54     if(SIGALRM == signo)
 55     {
 56         int i = 0;
 57         for(i=0; i<SECON_PTH_NUMS; i++)
 58         {
 59             glbva.pth_exit_flg[i] = PTHEXIT;//设置为退出状态
 60         }
 61     }
 62     else if(SIGINT == signo)
 63     {
 64         exit(0);
 65     }
 66 }
 67 
 68 int main(void)
 69 {
 70     int fd = 0;
 71     int i = 0;
 72     int ret = 0;
 73         
 74     //打开文件,供线程操作,所有的线程(函数)可以共享打开的文件描述符
 75     fd = open("./file", O_RDWR|O_CREAT|O_TRUNC, 0664);
 76     if(fd == -1) print_err("open ./file fail", __LINE__, errno);
 77 
 78     /* 通过循环创建两个次线程 */
 79     for(i=0; i<SECON_PTH_NUMS; i++)
 80     {
 81         glbva.pth_arg[i].fd = fd;//保存文件描述符
 82         glbva.pth_arg[i].pthno = i; //我自己给的线程编号
 83                                    //创建好次线程后,讲次线程分离
 84         ret = pthread_create(&glbva.pth_arg[i].tid, &glbva.attr, pth_fun, (void *)&glbva.pth_arg[i]);    
 85         if(ret != 0) print_err("pthread_create fail", __LINE__, ret);
 86     }
 87 
 88     signal(SIGINT, signal_fun);
 89 
 90     /* 定时2秒,时间到后取消次线程 */
 91     signal(SIGALRM, signal_fun);
 92     alarm(2);    
 93 
 94     while(1)
 95     {
 96         write(fd, "hello ", 6);
 97         write(fd, "world\n", 6);
 98     }        
 99     
100     return 0;
101 }

View Code

pthread_exit

原型 

#include <pthread.h>
void pthread_exit(void *retval); 

功能

线程调用这个函数时,可以主动退出(终止)。这类似于exit函数,不过exit是终止整个进程的,而pthread_exit是终止次线程的。如果你在次线程里面调用错误,调用的是exit,整个进程就终止了。事实上线程也可以通过调动return来退出(终止),不过return和pthread_exit之间有一定的区别     

参数

retval:线程结束时的返回值。如果返回值很多时,就封装成一个结构体,返回结构体变量的地址即可。

返回值

代码演示

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <pthread.h>
  5 #include <string.h>
  6 #include <errno.h>
  7 #include <sys/types.h>
  8 #include <sys/stat.h>
  9 #include <fcntl.h>
 10 #include <signal.h>
 11 
 12 #define SECON_PTH_NUMS     2  //次线程数量
 13 #define PTHEXIT     -1    
 14 
 15 /* 传递给线程的参数 */
 16 typedef struct pthread_arg 
 17 {
 18     pthread_t tid;//存放线程tid
 19     int pthno;//我自己定义的编号
 20     int fd;//文件描述符
 21 }ptharg;
 22 
 23 struct gloable_va
 24 {
 25     ptharg pth_arg[SECON_PTH_NUMS];//结构体数组,每个元素会被当做参数传递给对应的次线程
 26     int pth_exit_flg[SECON_PTH_NUMS];//每个元素存放对应编号线程的退出状态
 27     pthread_attr_t attr;//存放线程新属性
 28 }glbva;
 29 
 30 void print_err(char *str, int line, int err_no)
 31 {
 32     printf("%d, %s:%s", line, str, strerror(err_no));
 33     exit(-1);
 34 }
 35 
 36 void *pth_fun(void *pth_arg)
 37 {    
 38     int fd = ((ptharg *)pth_arg)->fd;
 39     int pthno = ((ptharg *)pth_arg)->pthno;
 40     pthread_t tid = ((ptharg *)pth_arg)->tid;    
 41     while(1)
 42     {
 43         write(fd, "HELLO ", 6);
 44         write(fd, "WORLD\n", 6);
 45         //检测退出状态
 46         if(glbva.pth_exit_flg[pthno] == PTHEXIT) break;
 47     }
 48 
 49     pthread_exit(NULL);
 50 }
 51 
 52 void signal_fun(int signo)
 53 {
 54     if(SIGALRM == signo)
 55     {
 56         int i = 0;
 57         for(i=0; i<SECON_PTH_NUMS; i++)
 58         {
 59             glbva.pth_exit_flg[i] = PTHEXIT;//设置为退出状态
 60         }
 61     }
 62     else if(SIGINT == signo)
 63     {
 64         exit(0);
 65     }
 66 }
 67 
 68 int main(void)
 69 {
 70     int fd = 0;
 71     int i = 0;
 72     int ret = 0;
 73         
 74     //打开文件,供线程操作,所有的线程(函数)可以共享打开的文件描述符
 75     fd = open("./file", O_RDWR|O_CREAT|O_TRUNC, 0664);
 76     if(fd == -1) print_err("open ./file fail", __LINE__, errno);
 77 
 78     /* 通过循环创建两个次线程 */
 79     for(i=0; i<SECON_PTH_NUMS; i++)
 80     {
 81         glbva.pth_arg[i].fd = fd;//保存文件描述符
 82         glbva.pth_arg[i].pthno = i; //我自己给的线程编号
 83                                    //创建好次线程后,讲次线程分离
 84         ret = pthread_create(&glbva.pth_arg[i].tid, &glbva.attr, pth_fun, (void *)&glbva.pth_arg[i]);    
 85         if(ret != 0) print_err("pthread_create fail", __LINE__, ret);
 86     }
 87 
 88     signal(SIGINT, signal_fun);
 89 
 90     /* 定时2秒,时间到后取消次线程 */
 91     signal(SIGALRM, signal_fun);
 92     alarm(2);    
 93 
 94     while(1)
 95     {
 96         write(fd, "hello ", 6);
 97         write(fd, "world\n", 6);
 98     }        
 99     
100     return 0;
101 }

View Code

pthread_self

原型 

#include <pthread.h>
pthread_t pthread_self(void);

功能

线程获取自己的TID,类似于进程调用getpid()获取自己的PID一样。

参数

返回值

成功返回线程TID,失败返回非零错误号。

代码演示

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <pthread.h>
  5 #include <string.h>
  6 #include <errno.h>
  7 #include <sys/types.h>
  8 #include <sys/stat.h>
  9 #include <fcntl.h>
 10 #include <signal.h>
 11 
 12 #define SECON_PTH_NUMS     2  //次线程数量
 13 #define PTHEXIT     -1    
 14 
 15 /* 传递给线程的参数 */
 16 typedef struct pthread_arg 
 17 {
 18     pthread_t tid;//存放线程tid
 19     int pthno;//我自己定义的编号
 20     int fd;//文件描述符
 21 }ptharg;
 22 
 23 struct gloable_va
 24 {
 25     ptharg pth_arg[SECON_PTH_NUMS];//结构体数组,每个元素会被当做参数传递给对应的次线程
 26     int pth_exit_flg[SECON_PTH_NUMS];//每个元素存放对应编号线程的退出状态
 27     pthread_attr_t attr;//存放线程新属性
 28 }glbva;
 29 
 30 void print_err(char *str, int line, int err_no)
 31 {
 32     printf("%d, %s:%s", line, str, strerror(err_no));
 33     exit(-1);
 34 }
 35 
 36 void *pth_fun(void *pth_arg)
 37 {    
 38     int fd = ((ptharg *)pth_arg)->fd;
 39     int pthno = ((ptharg *)pth_arg)->pthno;
 40     pthread_t tid = ((ptharg *)pth_arg)->tid;    
 41     while(1)
 42     {
 43         write(fd, "HELLO ", 6);
 44         write(fd, "WORLD\n", 6);
 45         //检测退出状态
 46         if(glbva.pth_exit_flg[pthno] == PTHEXIT) break;
 47     }
 48 
 49     pthread_exit(NULL);
 50 }
 51 
 52 void signal_fun(int signo)
 53 {
 54     if(SIGALRM == signo)
 55     {
 56         int i = 0;
 57         for(i=0; i<SECON_PTH_NUMS; i++)
 58         {
 59             glbva.pth_exit_flg[i] = PTHEXIT;//设置为退出状态
 60         }
 61     }
 62     else if(SIGINT == signo)
 63     {
 64         exit(0);
 65     }
 66 }
 67 
 68 int main(void)
 69 {
 70     int fd = 0;
 71     int i = 0;
 72     int ret = 0;
 73         
 74     //打开文件,供线程操作,所有的线程(函数)可以共享打开的文件描述符
 75     fd = open("./file", O_RDWR|O_CREAT|O_TRUNC, 0664);
 76     if(fd == -1) print_err("open ./file fail", __LINE__, errno);
 77 
 78     /* 通过循环创建两个次线程 */
 79     for(i=0; i<SECON_PTH_NUMS; i++)
 80     {
 81         glbva.pth_arg[i].fd = fd;//保存文件描述符
 82         glbva.pth_arg[i].pthno = i; //我自己给的线程编号
 83                                    //创建好次线程后,讲次线程分离
 84         ret = pthread_create(&glbva.pth_arg[i].tid, &glbva.attr, pth_fun, (void *)&glbva.pth_arg[i]);    
 85         if(ret != 0) print_err("pthread_create fail", __LINE__, ret);
 86     }
 87 
 88     printf("main tid = %lu\n", pthread_self());
 89     
 90     signal(SIGINT, signal_fun);
 91 
 92     /* 定时2秒,时间到后取消次线程 */
 93     signal(SIGALRM, signal_fun);
 94     alarm(2);    
 95 
 96     while(1)
 97     {
 98         write(fd, "hello ", 6);
 99         write(fd, "world\n", 6);
100     }        
101     
102     return 0;
103 }

View Code

pthread_join

原型 

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval); 

功能

阻塞等待tid为thread的次线程结束,结束时该函数会回收次线程所占用的所有资源(存储空间)。

这个函数只对次线程有意义,对主线程没有意义,因为主线程结束时整个进程就结束了,整个进程资源会由父进程回收。

疑问:线程所用内存资源本身就是来自于进程空间,既然进程结束后,整个进程的资源都会被回收掉,次线程结束时干嘛要回收资源,等进程结束后一并回收不就得了吗?

:有些程序(进程)一旦运行后将会长期运行,不会结束,所以次线程在结束时必须回收资源,如果不回收,每结束一个次线程就导致一部分资源被占用,慢慢累积会使得整个进程资源越用越少,最后导致进程崩溃,所以次线程结束时,必须回收次线程资源。这个函数一般都        是由主线程调用,以回收相关次线程的资源,当然次线程也是可以调用这个函数来回收其它次线程资源的。

参数

thread:指定要回收次线程的TID

retval:次线程函数返回的返回值。线程函数返回值是void *

返回值

成功返回0,失败返回错误号。

代码演示

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <pthread.h>
  5 #include <string.h>
  6 #include <errno.h>
  7 #include <sys/types.h>
  8 #include <sys/stat.h>
  9 #include <fcntl.h>
 10 #include <signal.h>
 11 
 12 #define SECON_PTH_NUMS     2  //次线程数量
 13 #define PTHEXIT     -1    
 14 
 15 /* 传递给线程的参数 */
 16 typedef struct pthread_arg 
 17 {
 18     pthread_t tid;//存放线程tid
 19     int pthno;//我自己定义的编号
 20     int fd;//文件描述符
 21 }ptharg;
 22 
 23 struct gloable_va
 24 {
 25     ptharg pth_arg[SECON_PTH_NUMS];//结构体数组,每个元素会被当做参数传递给对应的次线程
 26     int pth_exit_flg[SECON_PTH_NUMS];//每个元素存放对应编号线程的退出状态
 27     pthread_attr_t attr;//存放线程新属性
 28 }glbva;
 29 
 30 void print_err(char *str, int line, int err_no)
 31 {
 32     printf("%d, %s:%s", line, str, strerror(err_no));
 33     exit(-1);
 34 }
 35 
 36 void *pth_fun(void *pth_arg)
 37 {    
 38     int fd = ((ptharg *)pth_arg)->fd;
 39     int pthno = ((ptharg *)pth_arg)->pthno;
 40     pthread_t tid = ((ptharg *)pth_arg)->tid;    
 41     while(1)
 42     {
 43         write(fd, "HELLO ", 6);
 44         write(fd, "WORLD\n", 6);
 45         //检测退出状态
 46         if(glbva.pth_exit_flg[pthno] == PTHEXIT) break;
 47     }
 48 
 49     pthread_exit((void *)1);
 50 }
 51 
 52 void signal_fun(int signo)
 53 {
 54     if(SIGALRM == signo)
 55     {
 56         int i = 0;
 57         for(i=0; i<SECON_PTH_NUMS; i++)
 58         {
 59             glbva.pth_exit_flg[i] = PTHEXIT;//设置为退出状态
 60         }
 61     }
 62     else if(SIGINT == signo)
 63     {
 64         exit(0);
 65     }
 66 }
 67 
 68 int main(void)
 69 {
 70     int fd = 0;
 71     int i = 0;
 72     int ret = 0;
 73     void *retval = NULL;
 74         
 75     //打开文件,供线程操作,所有的线程(函数)可以共享打开的文件描述符
 76     fd = open("./file", O_RDWR|O_CREAT|O_TRUNC, 0664);
 77     if(fd == -1) print_err("open ./file fail", __LINE__, errno);
 78 
 79     /* 通过循环创建两个次线程 */
 80     for(i=0; i<SECON_PTH_NUMS; i++)
 81     {
 82         glbva.pth_arg[i].fd = fd;//保存文件描述符
 83         glbva.pth_arg[i].pthno = i; //我自己给的线程编号
 84                                    //创建好次线程后,讲次线程分离
 85         ret = pthread_create(&glbva.pth_arg[i].tid, &glbva.attr, pth_fun, (void *)&glbva.pth_arg[i]);    
 86         if(ret != 0) print_err("pthread_create fail", __LINE__, ret);
 87     }
 88 
 89     printf("main tid = %lu\n", pthread_self());
 90     
 91     signal(SIGINT, signal_fun);
 92 
 93     /* 定时2秒,时间到后取消次线程 */
 94     signal(SIGALRM, signal_fun);
 95     alarm(2);    
 96 
 97     for(i=0; i<SECON_PTH_NUMS; i++)
 98     {
 99         //阻塞等待此线程结束,回收次线程资源,并通过第二个参数接受返回值
100         pthread_join(glbva.pth_arg[i].tid, &retval);
101         printf("@@ %ld\n", (long)retval);
102     }
103     
104     while(1)
105     {
106         write(fd, "hello ", 6);
107         write(fd, "world\n", 6);
108     }        
109     
110     return 0;
111 }

View Code

如果线程是被pthread_cancel取消掉的,自动返回-1。

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <pthread.h>
  5 #include <string.h>
  6 #include <errno.h>
  7 #include <sys/types.h>
  8 #include <sys/stat.h>
  9 #include <fcntl.h>
 10 #include <signal.h>
 11 
 12 #define SECON_PTH_NUMS     2  //次线程数量
 13 #define PTHEXIT     -1    
 14 
 15 /* 传递给线程的参数 */
 16 typedef struct pthread_arg 
 17 {
 18     pthread_t tid;//存放线程tid
 19     int pthno;//我自己定义的编号
 20     int fd;//文件描述符
 21 }ptharg;
 22 
 23 struct gloable_va
 24 {
 25     ptharg pth_arg[SECON_PTH_NUMS];//结构体数组,每个元素会被当做参数传递给对应的次线程
 26     int pth_exit_flg[SECON_PTH_NUMS];//每个元素存放对应编号线程的退出状态
 27     pthread_attr_t attr;//存放线程新属性
 28 }glbva;
 29 
 30 void print_err(char *str, int line, int err_no)
 31 {
 32     printf("%d, %s:%s", line, str, strerror(err_no));
 33     exit(-1);
 34 }
 35 
 36 void *pth_fun(void *pth_arg)
 37 {    
 38     int fd = ((ptharg *)pth_arg)->fd;
 39     int pthno = ((ptharg *)pth_arg)->pthno;
 40     pthread_t tid = ((ptharg *)pth_arg)->tid;    
 41     while(1)
 42     {
 43         write(fd, "HELLO ", 6);
 44         write(fd, "WORLD\n", 6);
 45         //检测退出状态
 46         if(glbva.pth_exit_flg[pthno] == PTHEXIT) break;
 47     }
 48 
 49     pthread_exit((void *)1);
 50 }
 51 
 52 void signal_fun(int signo)
 53 {
 54     if(SIGALRM == signo)
 55     {
 56         int i = 0;
 57         for(i=0; i<SECON_PTH_NUMS; i++)
 58         {
 59             pthread_cancel(glbva.pth_arg[i].tid);//取消次线程
 60             //glbva.pth_exit_flg[i] = PTHEXIT;//设置为退出状态
 61         }
 62     }
 63     else if(SIGINT == signo)
 64     {
 65         exit(0);
 66     }
 67 }
 68 
 69 int main(void)
 70 {
 71     int fd = 0;
 72     int i = 0;
 73     int ret = 0;
 74     void *retval = NULL;
 75         
 76     //打开文件,供线程操作,所有的线程(函数)可以共享打开的文件描述符
 77     fd = open("./file", O_RDWR|O_CREAT|O_TRUNC, 0664);
 78     if(fd == -1) print_err("open ./file fail", __LINE__, errno);
 79 
 80     /* 通过循环创建两个次线程 */
 81     for(i=0; i<SECON_PTH_NUMS; i++)
 82     {
 83         glbva.pth_arg[i].fd = fd;//保存文件描述符
 84         glbva.pth_arg[i].pthno = i; //我自己给的线程编号
 85                                    //创建好次线程后,讲次线程分离
 86         ret = pthread_create(&glbva.pth_arg[i].tid, &glbva.attr, pth_fun, (void *)&glbva.pth_arg[i]);    
 87         if(ret != 0) print_err("pthread_create fail", __LINE__, ret);
 88     }
 89 
 90     printf("main tid = %lu\n", pthread_self());
 91     
 92     signal(SIGINT, signal_fun);
 93 
 94     /* 定时2秒,时间到后取消次线程 */
 95     signal(SIGALRM, signal_fun);
 96     alarm(2);    
 97 
 98     for(i=0; i<SECON_PTH_NUMS; i++)
 99     {
100         //阻塞等待此线程结束,回收次线程资源,并通过第二个参数接受返回值
101         pthread_join(glbva.pth_arg[i].tid, &retval);
102         printf("@@ %ld\n", (long)retval);
103     }
104     
105     while(1)
106     {
107         write(fd, "hello ", 6);
108         write(fd, "world\n", 6);
109     }        
110     
111     return 0;
112 }

View Code

pthread_detach

原型 

#include <pthread.h>             
int pthread_detach(pthread_t thread);

功能

如果次线程的资源不希望别人调用pthread_join函数来回收的话,而是希望自己在结束时,自动回收资源的话,就可以调用这个函数。这个函数的功能就是分离次线程,让次线程在结束时自动回收资源。

参数

thread:你要分离的那个次线程的TID。

返回值

成功返回0,失败返回错误号。

代码演示

pthread_join回收线程资源,由于是阻塞回收,在次线程没有结束的时候,调用pthread_join发生在主线程中,主线程会一直阻塞等,直到次线程结束为止。主线程后面代码while循环得不到执行。如果既想主线程后面while得到执行,次线程结束时资源也被回收,可以分离次线程,这样次线程结束时自己回收资源。这样有个弊端,不调用pthread_join,就得不到次线程结束时返回值。pthread_join和pthread_detach回收资源方式只能选择一种。

法1

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <pthread.h>
  5 #include <string.h>
  6 #include <errno.h>
  7 #include <sys/types.h>
  8 #include <sys/stat.h>
  9 #include <fcntl.h>
 10 #include <signal.h>
 11 
 12 #define SECON_PTH_NUMS     2  //次线程数量
 13 #define PTHEXIT     -1    
 14 
 15 /* 传递给线程的参数 */
 16 typedef struct pthread_arg 
 17 {
 18     pthread_t tid;//存放线程tid
 19     int pthno;//我自己定义的编号
 20     int fd;//文件描述符
 21 }ptharg;
 22 
 23 struct gloable_va
 24 {
 25     ptharg pth_arg[SECON_PTH_NUMS];//结构体数组,每个元素会被当做参数传递给对应的次线程
 26     int pth_exit_flg[SECON_PTH_NUMS];//每个元素存放对应编号线程的退出状态
 27     pthread_attr_t attr;//存放线程新属性
 28 }glbva;
 29 
 30 void print_err(char *str, int line, int err_no)
 31 {
 32     printf("%d, %s:%s", line, str, strerror(err_no));
 33     exit(-1);
 34 }
 35 
 36 void *pth_fun(void *pth_arg)
 37 {    
 38     int fd = ((ptharg *)pth_arg)->fd;
 39     int pthno = ((ptharg *)pth_arg)->pthno;
 40     pthread_t tid = ((ptharg *)pth_arg)->tid;    
 41     while(1)
 42     {
 43         write(fd, "HELLO ", 6);
 44         write(fd, "WORLD\n", 6);
 45         //检测退出状态
 46         if(glbva.pth_exit_flg[pthno] == PTHEXIT) break;
 47     }
 48 
 49     pthread_exit((void *)1);
 50 }
 51 
 52 void signal_fun(int signo)
 53 {
 54     if(SIGALRM == signo)
 55     {
 56         int i = 0;
 57         for(i=0; i<SECON_PTH_NUMS; i++)
 58         {
 59             pthread_cancel(glbva.pth_arg[i].tid);//取消次线程
 60             //glbva.pth_exit_flg[i] = PTHEXIT;//设置为退出状态
 61         }
 62     }
 63     else if(SIGINT == signo)
 64     {
 65         exit(0);
 66     }
 67 }
 68 
 69 int main(void)
 70 {
 71     int fd = 0;
 72     int i = 0;
 73     int ret = 0;
 74     void *retval = NULL;
 75         
 76     //打开文件,供线程操作,所有的线程(函数)可以共享打开的文件描述符
 77     fd = open("./file", O_RDWR|O_CREAT|O_TRUNC, 0664);
 78     if(fd == -1) print_err("open ./file fail", __LINE__, errno);
 79 
 80     /* 通过循环创建两个次线程 */
 81     for(i=0; i<SECON_PTH_NUMS; i++)
 82     {
 83         glbva.pth_arg[i].fd = fd;//保存文件描述符
 84         glbva.pth_arg[i].pthno = i; //我自己给的线程编号
 85                                    //创建好次线程后,讲次线程分离
 86         ret = pthread_create(&glbva.pth_arg[i].tid, &glbva.attr, pth_fun, (void *)&glbva.pth_arg[i]);    
 87         if(ret != 0) print_err("pthread_create fail", __LINE__, ret);
 88         pthread_detach(glbva.pth_arg[i].tid);
 89     }
 90 
 91     printf("main tid = %lu\n", pthread_self());
 92     
 93     signal(SIGINT, signal_fun);
 94 
 95     /* 定时2秒,时间到后取消次线程 */
 96     signal(SIGALRM, signal_fun);
 97     alarm(2);    
 98 
 99     #if 0
100     for(i=0; i<SECON_PTH_NUMS; i++)
101     {
102         //阻塞等待此线程结束,回收次线程资源,并通过第二个参数接受返回值
103         pthread_join(glbva.pth_arg[i].tid, &retval);
104         printf("@@ %ld\n", (long)retval);
105     }
106     #endif
107     
108     while(1)
109     {
110         write(fd, "hello ", 6);
111         write(fd, "world\n", 6);
112     }        
113     
114     return 0;
115 }

View Code

法2

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>

#define SECON_PTH_NUMS     2  //次线程数量
#define PTHEXIT     -1    

/* 传递给线程的参数 */
typedef struct pthread_arg 
{
    pthread_t tid;//存放线程tid
    int pthno;//我自己定义的编号
    int fd;//文件描述符
}ptharg;

struct gloable_va
{
    ptharg pth_arg[SECON_PTH_NUMS];//结构体数组,每个元素会被当做参数传递给对应的次线程
    int pth_exit_flg[SECON_PTH_NUMS];//每个元素存放对应编号线程的退出状态
    pthread_attr_t attr;//存放线程新属性
}glbva;

void print_err(char *str, int line, int err_no)
{
    printf("%d, %s:%s", line, str, strerror(err_no));
    exit(-1);
}

void *pth_fun(void *pth_arg)
{    
    int fd = ((ptharg *)pth_arg)->fd;
    int pthno = ((ptharg *)pth_arg)->pthno;
    pthread_t tid = ((ptharg *)pth_arg)->tid;    
    //pthread_detach(tid);  2种方法
    pthread_detach(pthread_self());
    while(1)
    {
        write(fd, "HELLO ", 6);
        write(fd, "WORLD\n", 6);
        //检测退出状态
        if(glbva.pth_exit_flg[pthno] == PTHEXIT) break;
    }

    pthread_exit((void *)1);
}

void signal_fun(int signo)
{
    if(SIGALRM == signo)
    {
        int i = 0;
        for(i=0; i<SECON_PTH_NUMS; i++)
        {
            pthread_cancel(glbva.pth_arg[i].tid);//取消次线程
            //glbva.pth_exit_flg[i] = PTHEXIT;//设置为退出状态
        }
    }
    else if(SIGINT == signo)
    {
        exit(0);
    }
}

int main(void)
{
    int fd = 0;
    int i = 0;
    int ret = 0;
    void *retval = NULL;
        
    //打开文件,供线程操作,所有的线程(函数)可以共享打开的文件描述符
    fd = open("./file", O_RDWR|O_CREAT|O_TRUNC, 0664);
    if(fd == -1) print_err("open ./file fail", __LINE__, errno);

    /* 通过循环创建两个次线程 */
    for(i=0; i<SECON_PTH_NUMS; i++)
    {
        glbva.pth_arg[i].fd = fd;//保存文件描述符
        glbva.pth_arg[i].pthno = i; //我自己给的线程编号
                                   //创建好次线程后,讲次线程分离
        ret = pthread_create(&glbva.pth_arg[i].tid, &glbva.attr, pth_fun, (void *)&glbva.pth_arg[i]);    
        if(ret != 0) print_err("pthread_create fail", __LINE__, ret);
    }

    printf("main tid = %lu\n", pthread_self());
    
    signal(SIGINT, signal_fun);

    /* 定时2秒,时间到后取消次线程 */
    signal(SIGALRM, signal_fun);
    alarm(2);    

    #if 0
    for(i=0; i<SECON_PTH_NUMS; i++)
    {
        //阻塞等待此线程结束,回收次线程资源,并通过第二个参数接受返回值
        pthread_join(glbva.pth_arg[i].tid, &retval);
        printf("@@ %ld\n", (long)retval);
    }
    #endif
    
    while(1)
    {
        write(fd, "hello ", 6);
        write(fd, "world\n", 6);
    }        
    
    return 0;
}

View Code

 注册线程退出处理函数

进程结束时会自动调用进程退出处理函数,进程退出处理函数使用atexit注册,当执行exit(0)的时候会调用进程退出处理函数做进程结束扫尾工作。

同样可以注册线程退出处理函数,线程在退出时会自动调用,实现线程的扫尾处理。

原型

void pthread_cleanup_push(void (*routine)(void *), void *arg);
void pthread_cleanup_pop(int execute); 

功能

pthread_cleanup_push

将类型为void (*routine)(void *)函数注册为“线程退出处理函数”,arg为传递给退出处理函数的参数。注册的原理就是将处理函数地址压入线程栈。我们可以反复调用该函数注册多个退出处理函数,但是一般一个就够了。

pthread_cleanup_pop

执行这个函数时,如果参数写!0:会将压入栈中的推出处理函数地址弹出,然后调用退出函数进行线程的扫尾处理。如果参数写0:不弹出调用

如果注册了多个线程退出处理函数的话,由于栈先进后出的特点,所以注册压栈的顺序与弹栈调动的顺序刚好相反。

这个函数必须和pthread_cleanup_push配对出现,有一个pthread_cleanup_push,就必须要对应有一个pthread_cleanup_pop,就算这个函数调用不到也必须写,否者编译时不通过,这就好比{}是成对出现的,缺一个都会报错。

代码演示

使用pthread_cancel粗暴终止线程

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <pthread.h>
  5 #include <string.h>
  6 #include <errno.h>
  7 #include <sys/types.h>
  8 #include <sys/stat.h>
  9 #include <fcntl.h>
 10 #include <signal.h>
 11 
 12 
 13 #define SECON_PTH_NUMS     2  //次线程数量
 14 #define PTHEXIT     -1    
 15 
 16 
 17 /* 传递给线程的参数 */
 18 typedef struct pthread_arg 
 19 {
 20     pthread_t tid;//存放线程tid
 21     int pthno;//我自己定义的编号
 22     int fd;//文件描述符
 23 }ptharg;
 24 
 25 
 26 
 27 struct gloable_va
 28 {
 29     ptharg pth_arg[SECON_PTH_NUMS];//结构体数组,每个元素会被当做参数传递给对应的次线程
 30     int pth_exit_flg[SECON_PTH_NUMS];//每个元素存放对应编号线程的退出状态
 31     pthread_attr_t attr;//存放线程新属性
 32 }glbva;
 33 
 34 void print_err(char *str, int line, int err_no)
 35 {
 36     printf("%d, %s:%s", line, str, strerror(err_no));
 37     exit(-1);
 38 }
 39 
 40 /* 线程退出处理函数 */
 41 void pth_exit_deal(void *arg)
 42 {
 43     pthread_t tid = ((ptharg *)arg)->tid;    
 44 
 45     printf("!!! pthread %lu exit\n", tid);
 46 }
 47 
 48 void *pth_fun(void *pth_arg)
 49 {    
 50     int fd = ((ptharg *)pth_arg)->fd;
 51     int pthno = ((ptharg *)pth_arg)->pthno;
 52     pthread_t tid = ((ptharg *)pth_arg)->tid;    
 53 
 54     //pthread_detach(pthread_self());//线程把自己分离出去
 55 
 56     //注册线程退出处理函数
 57     pthread_cleanup_push(pth_exit_deal, pth_arg);
 58     
 59     printf("pthno=%d, pthread_id=%lu\n", pthno, tid);
 60 
 61     while(1)
 62     {
 63         write(fd, "hello ", 6);
 64         write(fd, "world\n", 6);
 65         //检测退出状态
 66         if(glbva.pth_exit_flg[pthno] == PTHEXIT) break;
 67     }
 68 
 69     
 70     pthread_cleanup_pop(!0);    
 71     //return NULL;
 72     pthread_exit((void *)10);     
 73 }
 74 
 75 void signal_fun(int signo)
 76 {
 77     if(SIGALRM == signo)
 78     {
 79         int i = 0;
 80         for(i=0; i<SECON_PTH_NUMS; i++)
 81         {
 82             pthread_cancel(glbva.pth_arg[i].tid);//取消次线程
 83             //glbva.pth_exit_flg[i] = PTHEXIT;//设置为退出状态
 84         }
 85     }
 86     else if(SIGINT == signo)
 87     {
 88         exit(0);
 89     }
 90 }
 91 
 92 void process_exit_deal(void)
 93 {
 94     /* 销毁线程的属性设置 */
 95     int ret = 0;
 96     ret = pthread_attr_destroy(&glbva.attr);
 97     if(ret != 0) print_err("pthread_attr_destroy fail", __LINE__, ret);
 98     
 99     printf("\nprocess exit\n");
100 }
101 
102 int main(void)
103 {
104     int fd = 0;
105     int i = 0;
106     int ret = 0;
107     
108     //注册进程退出处理函数,exit正常终止进程时弹栈调用
109     atexit(process_exit_deal);
110         
111     //打开文件,供线程操作,所有的线程(函数)可以共享打开的文件描述符
112     fd = open("./file", O_RDWR|O_CREAT|O_TRUNC, 0664);
113     if(fd == -1) print_err("open ./file fail", __LINE__, errno);
114     
115     /* 初始化护attr, 设置一些基本的初始值 */
116     ret = pthread_attr_init(&glbva.attr);
117     if(ret != 0) print_err("pthread_attr_init fail", __LINE__, ret);
118         
119     /* 设置分离属性 */
120     ret = pthread_attr_setdetachstate(&glbva.attr, PTHREAD_CREATE_DETACHED);
121     if(ret != 0) print_err("pthread_attr_setdetachstate fail", __LINE__, ret);
122 
123     /* 通过循环创建两个次线程 */
124     for(i=0; i<SECON_PTH_NUMS; i++)
125     {
126         glbva.pth_arg[i].fd = fd;//保存文件描述符
127         glbva.pth_arg[i].pthno = i; //我自己给的线程编号
128                                    //创建好次线程后,讲次线程分离
129         ret = pthread_create(&glbva.pth_arg[i].tid, &glbva.attr, pth_fun, (void *)&glbva.pth_arg[i]);    
130         if(ret != 0) print_err("pthread_create fail", __LINE__, ret);
131     }
132 
133     printf("main tid = %lu\n", pthread_self());
134 
135     signal(SIGINT, signal_fun);
136 
137     /* 定时3秒,时间到后取消次线程 */
138     signal(SIGALRM, signal_fun);
139     alarm(3);    
140 
141     #if 0
142     void *retval = NULL;
143     for(i=0; i<SECON_PTH_NUMS; i++)
144     {
145         //阻塞等待此线程结束,回收次线程资源,并通过第二个参数接受返回值
146         pthread_join(glbva.pth_arg[i].tid, &retval);
147         printf("@@ %ld\n", (long)retval);
148     }
149     #endif
150         
151 
152     while(1)
153     {
154         write(fd, "hello ", 6);
155         write(fd, "world\n", 6);
156     }        
157     
158     return 0;
159 }

View Code

输出结果

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

1 [root@localhost ~]# ./a.out 
2 pthno=0, pthread_id=140160521762560
3 pthno=1, pthread_id=140160513369856
4 main tid = 140160530089792
5 !!! pthread 140160513369856 exit
6 !!! pthread 140160521762560 exit
7 ^C
8 process exit

View Code

这个代码及调用了进程退出处理函数,也调用了线程退出处理函数。

由于线程是调用pthread_cancel终结自己的,pthread_cancel函数的特点可能导致线程在第70行代码pthread_cleanup_pop(!0); 之前终结,pthread_cleanup_pop(!0); 没机会执行。但是由结果可知,线程退出处理函数被调用了。说明,使用pthread_cancel终结线程时,不管有没有调用pthread_cleanup_pop(!0);,线程结束时都会弹栈调用线程退出处理函数。

使用线程推出标志终止线程

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <pthread.h>
  5 #include <string.h>
  6 #include <errno.h>
  7 #include <sys/types.h>
  8 #include <sys/stat.h>
  9 #include <fcntl.h>
 10 #include <signal.h>
 11 
 12 
 13 #define SECON_PTH_NUMS     2  //次线程数量
 14 #define PTHEXIT     -1    
 15 
 16 
 17 /* 传递给线程的参数 */
 18 typedef struct pthread_arg 
 19 {
 20     pthread_t tid;//存放线程tid
 21     int pthno;//我自己定义的编号
 22     int fd;//文件描述符
 23 }ptharg;
 24 
 25 
 26 
 27 struct gloable_va
 28 {
 29     ptharg pth_arg[SECON_PTH_NUMS];//结构体数组,每个元素会被当做参数传递给对应的次线程
 30     int pth_exit_flg[SECON_PTH_NUMS];//每个元素存放对应编号线程的退出状态
 31     pthread_attr_t attr;//存放线程新属性
 32 }glbva;
 33 
 34 void print_err(char *str, int line, int err_no)
 35 {
 36     printf("%d, %s:%s", line, str, strerror(err_no));
 37     exit(-1);
 38 }
 39 
 40 /* 线程退出处理函数 */
 41 void pth_exit_deal(void *arg)
 42 {
 43     pthread_t tid = ((ptharg *)arg)->tid;    
 44 
 45     printf("!!! pthread %lu exit\n", tid);
 46 }
 47 
 48 void *pth_fun(void *pth_arg)
 49 {    
 50     int fd = ((ptharg *)pth_arg)->fd;
 51     int pthno = ((ptharg *)pth_arg)->pthno;
 52     pthread_t tid = ((ptharg *)pth_arg)->tid;    
 53 
 54     //pthread_detach(pthread_self());//线程把自己分离出去
 55 
 56     //注册线程退出处理函数
 57     pthread_cleanup_push(pth_exit_deal, pth_arg);
 58     
 59     printf("pthno=%d, pthread_id=%lu\n", pthno, tid);
 60 
 61     while(1)
 62     {
 63         write(fd, "hello ", 6);
 64         write(fd, "world\n", 6);
 65         //检测退出状态
 66         if(glbva.pth_exit_flg[pthno] == PTHEXIT) break;
 67     }
 68 
 69     pthread_cleanup_pop(!0);    
 70     //return NULL;
 71     pthread_exit((void *)10);     
 72 }
 73 
 74 void signal_fun(int signo)
 75 {
 76     if(SIGALRM == signo)
 77     {
 78         int i = 0;
 79         for(i=0; i<SECON_PTH_NUMS; i++)
 80         {
 81             //pthread_cancel(glbva.pth_arg[i].tid);//取消次线程
 82             glbva.pth_exit_flg[i] = PTHEXIT;//设置为退出状态
 83         }
 84     }
 85     else if(SIGINT == signo)
 86     {
 87         exit(0);
 88     }
 89 }
 90 
 91 void process_exit_deal(void)
 92 {
 93     /* 销毁线程的属性设置 */
 94     int ret = 0;
 95     ret = pthread_attr_destroy(&glbva.attr);
 96     if(ret != 0) print_err("pthread_attr_destroy fail", __LINE__, ret);
 97     
 98     printf("\nprocess exit\n");
 99 }
100 
101 int main(void)
102 {
103     int fd = 0;
104     int i = 0;
105     int ret = 0;
106     
107     //注册进程退出处理函数,exit正常终止进程时弹栈调用
108     atexit(process_exit_deal);
109         
110     //打开文件,供线程操作,所有的线程(函数)可以共享打开的文件描述符
111     fd = open("./file", O_RDWR|O_CREAT|O_TRUNC, 0664);
112     if(fd == -1) print_err("open ./file fail", __LINE__, errno);
113     
114     /* 初始化护attr, 设置一些基本的初始值 */
115     ret = pthread_attr_init(&glbva.attr);
116     if(ret != 0) print_err("pthread_attr_init fail", __LINE__, ret);
117         
118     /* 设置分离属性 */
119     ret = pthread_attr_setdetachstate(&glbva.attr, PTHREAD_CREATE_DETACHED);
120     if(ret != 0) print_err("pthread_attr_setdetachstate fail", __LINE__, ret);
121 
122     /* 通过循环创建两个次线程 */
123     for(i=0; i<SECON_PTH_NUMS; i++)
124     {
125         glbva.pth_arg[i].fd = fd;//保存文件描述符
126         glbva.pth_arg[i].pthno = i; //我自己给的线程编号
127                                    //创建好次线程后,讲次线程分离
128         ret = pthread_create(&glbva.pth_arg[i].tid, &glbva.attr, pth_fun, (void *)&glbva.pth_arg[i]);    
129         if(ret != 0) print_err("pthread_create fail", __LINE__, ret);
130     }
131 
132     printf("main tid = %lu\n", pthread_self());
133 
134     signal(SIGINT, signal_fun);
135 
136     /* 定时3秒,时间到后取消次线程 */
137     signal(SIGALRM, signal_fun);
138     alarm(3);    
139 
140     #if 0
141     void *retval = NULL;
142     for(i=0; i<SECON_PTH_NUMS; i++)
143     {
144         //阻塞等待此线程结束,回收次线程资源,并通过第二个参数接受返回值
145         pthread_join(glbva.pth_arg[i].tid, &retval);
146         printf("@@ %ld\n", (long)retval);
147     }
148     #endif
149         
150 
151     while(1)
152     {
153         write(fd, "hello ", 6);
154         write(fd, "world\n", 6);
155     }        
156     
157     return 0;
158 }

View Code

输出结果

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

1 [root@localhost ~]# ./a.out 
2 main tid = 140396089882432
3 pthno=1, pthread_id=140396073162496
4 pthno=0, pthread_id=140396081555200
5 !!! pthread 140396081555200 exit
6 !!! pthread 140396073162496 exit
7 ^C
8 process exit

View Code

当检测到线程退出状态为PTHEXIT时,便会break出while循环,执行pthread_cleanup_pop(!0);   线程退出处理函数弹栈被调用,线程扫尾。

在线程退出处理函数之前退出线程

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <pthread.h>
  5 #include <string.h>
  6 #include <errno.h>
  7 #include <sys/types.h>
  8 #include <sys/stat.h>
  9 #include <fcntl.h>
 10 #include <signal.h>
 11 
 12 
 13 #define SECON_PTH_NUMS     2  //次线程数量
 14 #define PTHEXIT     -1    
 15 
 16 
 17 /* 传递给线程的参数 */
 18 typedef struct pthread_arg 
 19 {
 20     pthread_t tid;//存放线程tid
 21     int pthno;//我自己定义的编号
 22     int fd;//文件描述符
 23 }ptharg;
 24 
 25 
 26 
 27 struct gloable_va
 28 {
 29     ptharg pth_arg[SECON_PTH_NUMS];//结构体数组,每个元素会被当做参数传递给对应的次线程
 30     int pth_exit_flg[SECON_PTH_NUMS];//每个元素存放对应编号线程的退出状态
 31     pthread_attr_t attr;//存放线程新属性
 32 }glbva;
 33 
 34 void print_err(char *str, int line, int err_no)
 35 {
 36     printf("%d, %s:%s", line, str, strerror(err_no));
 37     exit(-1);
 38 }
 39 
 40 /* 线程退出处理函数 */
 41 void pth_exit_deal(void *arg)
 42 {
 43     pthread_t tid = ((ptharg *)arg)->tid;    
 44 
 45     printf("!!! pthread %lu exit\n", tid);
 46 }
 47 
 48 void *pth_fun(void *pth_arg)
 49 {    
 50     int fd = ((ptharg *)pth_arg)->fd;
 51     int pthno = ((ptharg *)pth_arg)->pthno;
 52     pthread_t tid = ((ptharg *)pth_arg)->tid;    
 53 
 54     //pthread_detach(pthread_self());//线程把自己分离出去
 55 
 56     //注册线程退出处理函数
 57     pthread_cleanup_push(pth_exit_deal, pth_arg);
 58     
 59     printf("pthno=%d, pthread_id=%lu\n", pthno, tid);
 60 
 61     while(1)
 62     {
 63         write(fd, "hello ", 6);
 64         write(fd, "world\n", 6);
 65         //检测退出状态
 66         if(glbva.pth_exit_flg[pthno] == PTHEXIT) break;
 67     }
 68 
 69     pthread_exit((void *)10); 
 70     pthread_cleanup_pop(!0);    
 71     //return NULL;    
 72 }
 73 
 74 void signal_fun(int signo)
 75 {
 76     if(SIGALRM == signo)
 77     {
 78         int i = 0;
 79         for(i=0; i<SECON_PTH_NUMS; i++)
 80         {
 81             //pthread_cancel(glbva.pth_arg[i].tid);//取消次线程
 82             glbva.pth_exit_flg[i] = PTHEXIT;//设置为退出状态
 83         }
 84     }
 85     else if(SIGINT == signo)
 86     {
 87         exit(0);
 88     }
 89 }
 90 
 91 void process_exit_deal(void)
 92 {
 93     /* 销毁线程的属性设置 */
 94     int ret = 0;
 95     ret = pthread_attr_destroy(&glbva.attr);
 96     if(ret != 0) print_err("pthread_attr_destroy fail", __LINE__, ret);
 97     
 98     printf("\nprocess exit\n");
 99 }
100 
101 int main(void)
102 {
103     int fd = 0;
104     int i = 0;
105     int ret = 0;
106     
107     //注册进程退出处理函数,exit正常终止进程时弹栈调用
108     atexit(process_exit_deal);
109         
110     //打开文件,供线程操作,所有的线程(函数)可以共享打开的文件描述符
111     fd = open("./file", O_RDWR|O_CREAT|O_TRUNC, 0664);
112     if(fd == -1) print_err("open ./file fail", __LINE__, errno);
113     
114     /* 初始化护attr, 设置一些基本的初始值 */
115     ret = pthread_attr_init(&glbva.attr);
116     if(ret != 0) print_err("pthread_attr_init fail", __LINE__, ret);
117         
118     /* 设置分离属性 */
119     ret = pthread_attr_setdetachstate(&glbva.attr, PTHREAD_CREATE_DETACHED);
120     if(ret != 0) print_err("pthread_attr_setdetachstate fail", __LINE__, ret);
121 
122     /* 通过循环创建两个次线程 */
123     for(i=0; i<SECON_PTH_NUMS; i++)
124     {
125         glbva.pth_arg[i].fd = fd;//保存文件描述符
126         glbva.pth_arg[i].pthno = i; //我自己给的线程编号
127                                    //创建好次线程后,讲次线程分离
128         ret = pthread_create(&glbva.pth_arg[i].tid, &glbva.attr, pth_fun, (void *)&glbva.pth_arg[i]);    
129         if(ret != 0) print_err("pthread_create fail", __LINE__, ret);
130     }
131 
132     printf("main tid = %lu\n", pthread_self());
133 
134     signal(SIGINT, signal_fun);
135 
136     /* 定时3秒,时间到后取消次线程 */
137     signal(SIGALRM, signal_fun);
138     alarm(3);    
139 
140     #if 0
141     void *retval = NULL;
142     for(i=0; i<SECON_PTH_NUMS; i++)
143     {
144         //阻塞等待此线程结束,回收次线程资源,并通过第二个参数接受返回值
145         pthread_join(glbva.pth_arg[i].tid, &retval);
146         printf("@@ %ld\n", (long)retval);
147     }
148     #endif
149         
150 
151     while(1)
152     {
153         write(fd, "hello ", 6);
154         write(fd, "world\n", 6);
155     }        
156     
157     return 0;
158 }

View Code

输出结果

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

1 [root@localhost ~]# ./a.out 
2 pthno=0, pthread_id=140523134777088
3 main tid = 140523143104320
4 pthno=1, pthread_id=140523126384384
5 !!! pthread 140523126384384 exit
6 !!! pthread 140523134777088 exit
7 ^C
8 process exit

View Code

pthread_cleanup_pop函数还没执行呢,pthread_exit就使线程推出了。但是输出结果上看,很明显线程退出处理函数是弹栈执行了。说明线程如果是调用pthread_exit主动结束的话也可以自动弹栈。

线程以return退出

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <pthread.h>
  5 #include <string.h>
  6 #include <errno.h>
  7 #include <sys/types.h>
  8 #include <sys/stat.h>
  9 #include <fcntl.h>
 10 #include <signal.h>
 11 
 12 
 13 #define SECON_PTH_NUMS     2  //次线程数量
 14 #define PTHEXIT     -1    
 15 
 16 
 17 /* 传递给线程的参数 */
 18 typedef struct pthread_arg 
 19 {
 20     pthread_t tid;//存放线程tid
 21     int pthno;//我自己定义的编号
 22     int fd;//文件描述符
 23 }ptharg;
 24 
 25 
 26 
 27 struct gloable_va
 28 {
 29     ptharg pth_arg[SECON_PTH_NUMS];//结构体数组,每个元素会被当做参数传递给对应的次线程
 30     int pth_exit_flg[SECON_PTH_NUMS];//每个元素存放对应编号线程的退出状态
 31     pthread_attr_t attr;//存放线程新属性
 32 }glbva;
 33 
 34 void print_err(char *str, int line, int err_no)
 35 {
 36     printf("%d, %s:%s", line, str, strerror(err_no));
 37     exit(-1);
 38 }
 39 
 40 /* 线程退出处理函数 */
 41 void pth_exit_deal(void *arg)
 42 {
 43     pthread_t tid = ((ptharg *)arg)->tid;    
 44 
 45     printf("!!! pthread %lu exit\n", tid);
 46 }
 47 
 48 void *pth_fun(void *pth_arg)
 49 {    
 50     int fd = ((ptharg *)pth_arg)->fd;
 51     int pthno = ((ptharg *)pth_arg)->pthno;
 52     pthread_t tid = ((ptharg *)pth_arg)->tid;    
 53 
 54     //pthread_detach(pthread_self());//线程把自己分离出去
 55 
 56     //注册线程退出处理函数
 57     pthread_cleanup_push(pth_exit_deal, pth_arg);
 58     
 59     printf("pthno=%d, pthread_id=%lu\n", pthno, tid);
 60 
 61     while(1)
 62     {
 63         write(fd, "hello ", 6);
 64         write(fd, "world\n", 6);
 65         //检测退出状态
 66         if(glbva.pth_exit_flg[pthno] == PTHEXIT) break;
 67     }
 68 
 69     return NULL;
 70     pthread_cleanup_pop(!0);    
 71     pthread_exit((void *)10); 
 72 }
 73 
 74 void signal_fun(int signo)
 75 {
 76     if(SIGALRM == signo)
 77     {
 78         int i = 0;
 79         for(i=0; i<SECON_PTH_NUMS; i++)
 80         {
 81             //pthread_cancel(glbva.pth_arg[i].tid);//取消次线程
 82             glbva.pth_exit_flg[i] = PTHEXIT;//设置为退出状态
 83         }
 84     }
 85     else if(SIGINT == signo)
 86     {
 87         exit(0);
 88     }
 89 }
 90 
 91 void process_exit_deal(void)
 92 {
 93     /* 销毁线程的属性设置 */
 94     int ret = 0;
 95     ret = pthread_attr_destroy(&glbva.attr);
 96     if(ret != 0) print_err("pthread_attr_destroy fail", __LINE__, ret);
 97     
 98     printf("\nprocess exit\n");
 99 }
100 
101 int main(void)
102 {
103     int fd = 0;
104     int i = 0;
105     int ret = 0;
106     
107     //注册进程退出处理函数,exit正常终止进程时弹栈调用
108     atexit(process_exit_deal);
109         
110     //打开文件,供线程操作,所有的线程(函数)可以共享打开的文件描述符
111     fd = open("./file", O_RDWR|O_CREAT|O_TRUNC, 0664);
112     if(fd == -1) print_err("open ./file fail", __LINE__, errno);
113     
114     /* 初始化护attr, 设置一些基本的初始值 */
115     ret = pthread_attr_init(&glbva.attr);
116     if(ret != 0) print_err("pthread_attr_init fail", __LINE__, ret);
117         
118     /* 设置分离属性 */
119     ret = pthread_attr_setdetachstate(&glbva.attr, PTHREAD_CREATE_DETACHED);
120     if(ret != 0) print_err("pthread_attr_setdetachstate fail", __LINE__, ret);
121 
122     /* 通过循环创建两个次线程 */
123     for(i=0; i<SECON_PTH_NUMS; i++)
124     {
125         glbva.pth_arg[i].fd = fd;//保存文件描述符
126         glbva.pth_arg[i].pthno = i; //我自己给的线程编号
127                                    //创建好次线程后,讲次线程分离
128         ret = pthread_create(&glbva.pth_arg[i].tid, &glbva.attr, pth_fun, (void *)&glbva.pth_arg[i]);    
129         if(ret != 0) print_err("pthread_create fail", __LINE__, ret);
130     }
131 
132     printf("main tid = %lu\n", pthread_self());
133 
134     signal(SIGINT, signal_fun);
135 
136     /* 定时3秒,时间到后取消次线程 */
137     signal(SIGALRM, signal_fun);
138     alarm(3);    
139 
140     #if 0
141     void *retval = NULL;
142     for(i=0; i<SECON_PTH_NUMS; i++)
143     {
144         //阻塞等待此线程结束,回收次线程资源,并通过第二个参数接受返回值
145         pthread_join(glbva.pth_arg[i].tid, &retval);
146         printf("@@ %ld\n", (long)retval);
147     }
148     #endif
149         
150 
151     while(1)
152     {
153         write(fd, "hello ", 6);
154         write(fd, "world\n", 6);
155     }        
156     
157     return 0;
158 }

View Code

输出结果

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

1 [root@localhost ~]# ./a.out 
2 pthno=0, pthread_id=140710552893184
3 pthno=1, pthread_id=140710544500480
4 main tid = 140710561220416
5 ^C
6 process exit

View Code

没有调研线程退出处理函数。如果想弹栈,必须把pthread_cleanup_pop(!0);放在return前面,代码如下就正常了

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <pthread.h>
  5 #include <string.h>
  6 #include <errno.h>
  7 #include <sys/types.h>
  8 #include <sys/stat.h>
  9 #include <fcntl.h>
 10 #include <signal.h>
 11 
 12 
 13 #define SECON_PTH_NUMS     2  //次线程数量
 14 #define PTHEXIT     -1    
 15 
 16 
 17 /* 传递给线程的参数 */
 18 typedef struct pthread_arg 
 19 {
 20     pthread_t tid;//存放线程tid
 21     int pthno;//我自己定义的编号
 22     int fd;//文件描述符
 23 }ptharg;
 24 
 25 
 26 
 27 struct gloable_va
 28 {
 29     ptharg pth_arg[SECON_PTH_NUMS];//结构体数组,每个元素会被当做参数传递给对应的次线程
 30     int pth_exit_flg[SECON_PTH_NUMS];//每个元素存放对应编号线程的退出状态
 31     pthread_attr_t attr;//存放线程新属性
 32 }glbva;
 33 
 34 void print_err(char *str, int line, int err_no)
 35 {
 36     printf("%d, %s:%s", line, str, strerror(err_no));
 37     exit(-1);
 38 }
 39 
 40 /* 线程退出处理函数 */
 41 void pth_exit_deal(void *arg)
 42 {
 43     pthread_t tid = ((ptharg *)arg)->tid;    
 44 
 45     printf("!!! pthread %lu exit\n", tid);
 46 }
 47 
 48 void *pth_fun(void *pth_arg)
 49 {    
 50     int fd = ((ptharg *)pth_arg)->fd;
 51     int pthno = ((ptharg *)pth_arg)->pthno;
 52     pthread_t tid = ((ptharg *)pth_arg)->tid;    
 53 
 54     //pthread_detach(pthread_self());//线程把自己分离出去
 55 
 56     //注册线程退出处理函数
 57     pthread_cleanup_push(pth_exit_deal, pth_arg);
 58     
 59     printf("pthno=%d, pthread_id=%lu\n", pthno, tid);
 60 
 61     while(1)
 62     {
 63         write(fd, "hello ", 6);
 64         write(fd, "world\n", 6);
 65         //检测退出状态
 66         if(glbva.pth_exit_flg[pthno] == PTHEXIT) break;
 67     }
 68     
 69     pthread_cleanup_pop(!0);
 70     return NULL;
 71     pthread_exit((void *)10); 
 72 }
 73 
 74 void signal_fun(int signo)
 75 {
 76     if(SIGALRM == signo)
 77     {
 78         int i = 0;
 79         for(i=0; i<SECON_PTH_NUMS; i++)
 80         {
 81             //pthread_cancel(glbva.pth_arg[i].tid);//取消次线程
 82             glbva.pth_exit_flg[i] = PTHEXIT;//设置为退出状态
 83         }
 84     }
 85     else if(SIGINT == signo)
 86     {
 87         exit(0);
 88     }
 89 }
 90 
 91 void process_exit_deal(void)
 92 {
 93     /* 销毁线程的属性设置 */
 94     int ret = 0;
 95     ret = pthread_attr_destroy(&glbva.attr);
 96     if(ret != 0) print_err("pthread_attr_destroy fail", __LINE__, ret);
 97     
 98     printf("\nprocess exit\n");
 99 }
100 
101 int main(void)
102 {
103     int fd = 0;
104     int i = 0;
105     int ret = 0;
106     
107     //注册进程退出处理函数,exit正常终止进程时弹栈调用
108     atexit(process_exit_deal);
109         
110     //打开文件,供线程操作,所有的线程(函数)可以共享打开的文件描述符
111     fd = open("./file", O_RDWR|O_CREAT|O_TRUNC, 0664);
112     if(fd == -1) print_err("open ./file fail", __LINE__, errno);
113     
114     /* 初始化护attr, 设置一些基本的初始值 */
115     ret = pthread_attr_init(&glbva.attr);
116     if(ret != 0) print_err("pthread_attr_init fail", __LINE__, ret);
117         
118     /* 设置分离属性 */
119     ret = pthread_attr_setdetachstate(&glbva.attr, PTHREAD_CREATE_DETACHED);
120     if(ret != 0) print_err("pthread_attr_setdetachstate fail", __LINE__, ret);
121 
122     /* 通过循环创建两个次线程 */
123     for(i=0; i<SECON_PTH_NUMS; i++)
124     {
125         glbva.pth_arg[i].fd = fd;//保存文件描述符
126         glbva.pth_arg[i].pthno = i; //我自己给的线程编号
127                                    //创建好次线程后,讲次线程分离
128         ret = pthread_create(&glbva.pth_arg[i].tid, &glbva.attr, pth_fun, (void *)&glbva.pth_arg[i]);    
129         if(ret != 0) print_err("pthread_create fail", __LINE__, ret);
130     }
131 
132     printf("main tid = %lu\n", pthread_self());
133 
134     signal(SIGINT, signal_fun);
135 
136     /* 定时3秒,时间到后取消次线程 */
137     signal(SIGALRM, signal_fun);
138     alarm(3);    
139 
140     #if 0
141     void *retval = NULL;
142     for(i=0; i<SECON_PTH_NUMS; i++)
143     {
144         //阻塞等待此线程结束,回收次线程资源,并通过第二个参数接受返回值
145         pthread_join(glbva.pth_arg[i].tid, &retval);
146         printf("@@ %ld\n", (long)retval);
147     }
148     #endif
149         
150 
151     while(1)
152     {
153         write(fd, "hello ", 6);
154         write(fd, "world\n", 6);
155     }        
156     
157     return 0;
158 }

View Code

总结:弹栈线程退出处理函数的几种条件

调用thread_cleanup_pop(!0),主动弹栈。执行到thread_cleanup_pop(!0)这个函数一定会弹栈。但是就算没执行到这个函数,某些情况下也会自动弹栈

1、如果线程是被别人调用pthread_cancel取消的,也会弹栈

2、如果线程是调用pthread_exit函数退出的,也会弹栈

3、如果线程是调用return退出的话,是不会自动弹栈的,要弹栈的话,必须主动调动thread_cleanup_pop(!0)。且thread_cleanup_pop(!0)必须在return前面

设置线程属性

可以设置的线程属性有

设置绑定属性、设置分离属性、设置线程堆栈属性、设置线程调度优先级属性等等

如果我们什么属性都不设置,那么线程使用的就是默认属性,事实上默认属性所提供的功能就已经足够我们使用了,因此一般不需要设置什么特别的属性。C线程中设置属性的函数非常多,基本每一种属性都有独立的设置函数,由于很少进行属性设置,因此我们这里就不做详细讲解,这里只举一个设置分离属性来实现线程分离的例子。

将线程分离有两种方法

调用pthread_detach函数实现

通过设置分离属性实现

事实上使用pthread_detach更方便些,不过这里还是要介绍第二种方式,主要是想演示一下是如何来设置C线程属性,把这个例子理解了,实际上其它属性的设置都是类似的操作,自己上网查阅资料就能实现。

分离属性设置的步骤

定义一个变量来存放新属性

pthread_attr_t attr;

pthread_attr_t 这个类型实际上是一个结构体,被typedef重命名了。

调用int pthread_attr_init(pthread_attr_t *attr); 初始化一下attr结构体变量

pthread_attr_init(&attr);成功返回0,失败返回错误号。

调用int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

预设分离属性。

pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

指定PTHREAD_CREATE_DETACHED宏后,pthread_attr_setdetachstate函数会自动的将分离属性相关的数据设置到attr结构体变量的各个成员中。

如果想要预设其它属性,就必须调用其它属性相关的函数来实现。

调动pthread_create创建线程时,将attr传递给pthread_create,将线程分离

pthread_create(&pth, &attr, pth_fun, &arg);

删除属性设置

int pthread_attr_destroy(pthread_attr_t *attr);

pthread_attr_destroy(&attr);成功返回0,失败返回错误号。

代码演示

线程可以通过ipc通信吗_教育理论基础知识
线程可以通过ipc通信吗_教育理论基础知识

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <pthread.h>
  5 #include <string.h>
  6 #include <errno.h>
  7 #include <sys/types.h>
  8 #include <sys/stat.h>
  9 #include <fcntl.h>
 10 #include <signal.h>
 11 
 12 
 13 #define SECON_PTH_NUMS     2  //次线程数量
 14 #define PTHEXIT     -1    
 15 
 16 
 17 /* 传递给线程的参数 */
 18 typedef struct pthread_arg 
 19 {
 20     pthread_t tid;//存放线程tid
 21     int pthno;//我自己定义的编号
 22     int fd;//文件描述符
 23 }ptharg;
 24 
 25 
 26 
 27 struct gloable_va
 28 {
 29     ptharg pth_arg[SECON_PTH_NUMS];//结构体数组,每个元素会被当做参数传递给对应的次线程
 30     int pth_exit_flg[SECON_PTH_NUMS];//每个元素存放对应编号线程的退出状态
 31     pthread_attr_t attr;//存放线程新属性
 32 }glbva;
 33 
 34 void print_err(char *str, int line, int err_no)
 35 {
 36     printf("%d, %s:%s", line, str, strerror(err_no));
 37     exit(-1);
 38 }
 39 
 40 /* 线程退出处理函数 */
 41 void pth_exit_deal(void *arg)
 42 {
 43     pthread_t tid = ((ptharg *)arg)->tid;    
 44 
 45     printf("!!! pthread %lu exit\n", tid);
 46 }
 47 
 48 void *pth_fun(void *pth_arg)
 49 {    
 50     int fd = ((ptharg *)pth_arg)->fd;
 51     int pthno = ((ptharg *)pth_arg)->pthno;
 52     pthread_t tid = ((ptharg *)pth_arg)->tid;    
 53 
 54     //pthread_detach(pthread_self());//线程把自己分离出去
 55 
 56     //注册线程退出处理函数
 57     pthread_cleanup_push(pth_exit_deal, pth_arg);
 58     
 59     printf("pthno=%d, pthread_id=%lu\n", pthno, tid);
 60 
 61     while(1)
 62     {
 63         write(fd, "hello ", 6);
 64         write(fd, "world\n", 6);
 65         //检测退出状态
 66         if(glbva.pth_exit_flg[pthno] == PTHEXIT) break;
 67     }
 68 
 69     
 70     pthread_cleanup_pop(!0);    
 71     //return NULL;
 72     pthread_exit((void *)10);     
 73 }
 74 
 75 void signal_fun(int signo)
 76 {
 77     if(SIGALRM == signo)
 78     {
 79         int i = 0;
 80         for(i=0; i<SECON_PTH_NUMS; i++)
 81         {
 82             pthread_cancel(glbva.pth_arg[i].tid);//取消次线程
 83             //glbva.pth_exit_flg[i] = PTHEXIT;//设置为退出状态
 84         }
 85     }
 86     else if(SIGINT == signo)
 87     {
 88         exit(0);
 89     }
 90 }
 91 
 92 void process_exit_deal(void)
 93 {
 94     /* 销毁线程的属性设置 */
 95     int ret = 0;
 96     ret = pthread_attr_destroy(&glbva.attr);
 97     if(ret != 0) print_err("pthread_attr_destroy fail", __LINE__, ret);
 98     
 99     printf("\nprocess exit\n");
100 }
101 
102 int main(void)
103 {
104     int fd = 0;
105     int i = 0;
106     int ret = 0;
107     
108     //注册进程退出处理函数,exit正常终止进程时弹栈调用
109     atexit(process_exit_deal);
110         
111     //打开文件,供线程操作,所有的线程(函数)可以共享打开的文件描述符
112     fd = open("./file", O_RDWR|O_CREAT|O_TRUNC, 0664);
113     if(fd == -1) print_err("open ./file fail", __LINE__, errno);
114     
115     /* 初始化护attr, 设置一些基本的初始值 */
116     ret = pthread_attr_init(&glbva.attr);
117     if(ret != 0) print_err("pthread_attr_init fail", __LINE__, ret);
118         
119     /* 设置分离属性 */
120     ret = pthread_attr_setdetachstate(&glbva.attr, PTHREAD_CREATE_DETACHED);
121     if(ret != 0) print_err("pthread_attr_setdetachstate fail", __LINE__, ret);
122 
123     /* 通过循环创建两个次线程 */
124     for(i=0; i<SECON_PTH_NUMS; i++)
125     {
126         glbva.pth_arg[i].fd = fd;//保存文件描述符
127         glbva.pth_arg[i].pthno = i; //我自己给的线程编号
128                                    //创建好次线程后,讲次线程分离
129         ret = pthread_create(&glbva.pth_arg[i].tid, &glbva.attr, pth_fun, (void *)&glbva.pth_arg[i]);    
130         if(ret != 0) print_err("pthread_create fail", __LINE__, ret);
131     }
132 
133     printf("main tid = %lu\n", pthread_self());
134 
135     signal(SIGINT, signal_fun);
136 
137     /* 定时3秒,时间到后取消次线程 */
138     signal(SIGALRM, signal_fun);
139     alarm(3);    
140 
141     #if 0
142     void *retval = NULL;
143     for(i=0; i<SECON_PTH_NUMS; i++)
144     {
145         //阻塞等待此线程结束,回收次线程资源,并通过第二个参数接受返回值
146         pthread_join(glbva.pth_arg[i].tid, &retval);
147         printf("@@ %ld\n", (long)retval);
148     }
149     #endif
150         
151 
152     while(1)
153     {
154         write(fd, "hello ", 6);
155         write(fd, "world\n", 6);
156     }        
157     
158     return 0;
159 }

View Code

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/kelamoyujuzhen/p/9435386.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/107376.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 如何将本地文件复制到远程服务器听语音

    如何将本地文件复制到远程服务器听语音

  • 数据库置疑修复_sqlserver错误日志在哪里

    数据库置疑修复_sqlserver错误日志在哪里这篇文章在我电脑上呆得太久了,也不知道还有没有用

  • 常用字典_古汉语常用字典

    常用字典_古汉语常用字典huangyu888!@#$zhoujiakui223223jtserver1981@223wztelecom2008easygetyzdx123echina0228xiaxue123-$$4rf7uj3ed8ik!!changeme$$ebochai517qifengjc09001.1qa2ws3edwzcgame12wanzhonggamewanzhonggame…

  • LoadRunner教程(7)-LoadRunner 创建测试场景

    LoadRunner教程(7)-LoadRunner 创建测试场景首先打开controller创建一个场景,有手工场景和目标场景设置两个选项,先选择手工场景手工场景设置GlobalSchedule:Scenario初始化:所有用户同时初始化,每隔多少秒初始化多少用户,每个用户运行之前初始化启动用户:多少用户启动,同时启动,每隔多长时间启动多少用户运行时间:持续运行直到结束,持续运行时间用户退出:用户同时退出,每隔多长时间…

  • Java集合容器面试题(2020最新版)「建议收藏」

    Java集合容器面试题(2020最新版)「建议收藏」文章目录集合容器概述什么是集合集合的特点集合和数组的区别使用集合框架的好处常用的集合类有哪些?List,Set,Map三者的区别?List、Set、Map是否继承自Collection接口?List、Map、Set三个接口存取元素时,各有什么特点?集合框架底层数据结构哪些集合类是线程安全的?Java集合的快速失败机制“fail-fast”?怎么确保一个集合不能被修改?Collection…

  • linux之路由知识之ip route 命令中的疑惑[通俗易懂]

    linux之路由知识之ip route 命令中的疑惑[通俗易懂]1.基础知识1.1路由(Routing)1.1.1路由策略(使用iprule命令操作路由策略数据库)基于策略的路由比传统路由在功能上更强大,使用更灵活,它使网络管理员不仅能够根据目的地址而且能够根据报文大小、应用或IP源地址等属性来选择转发路径。iprule命令:Usage:iprule[list|add|del]SELECTORACTION(ad…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号