POSIX线程,线程的客户/服务通信(pthread_join,pthread_exit,pthread_detach,pthread_self)
我们所熟知的线程函数:
1,pthread_create,pthread_join,pthread_exit,pthread_detach,pthread_self
2,如何避免产生僵尸线程(进程)
3,多线程引发的客户/服务通信
POSIX线程库函数介绍:
1,与线程有关的函数构成了一个完整的系列,绝大多数的函数的开头都是以:“pthread_t”打头的
2,要使用这些库函数,我们必须引入头文件
3,链接这些线程函数库时,要使用编译器命令的“- lpthread”选项
函数:
1,线程创建函数pthread_create
1 2 3 4 5 6 |
|
thread:该参数是一个指针,当线程创建成功时,返回创建线程的ID
attr:用于指定线程的属性,NULL表示默认的属性
start_routine:该参数为一个函数指针,指向线程创建后要调用的函数。这个被线程调用的函数也
称为线程函数
arg:参数指向传递给线程函数的参数
注意的是:线程创建成功后,pthread_create函数返回0。若不为0则说明创建线程失败。常见的错误码为:
EAGAIN(表示系统限制创建新的线程,如:线程数目过多),EINVAL(表示第二个参数代表的
线程属性值非法。)
线程创建成功后,新创建的线程开始运行第3个参数所指向的函数,原来的线程继续运行
传统的一些函数是成功返回0,失败返回-1,并且对全局变量errno赋值以提示错误,而pthread
出错的时候不会设置全局变量errno(但是其它的POSIX函数会这样处理),而是将错误代码通过返回
值返回
而我们之前经常用的那个perror就是用来检测全局变量的,所以下面这个函数用不了了:
1 2 3 4 5 6 |
|
上面我们知道pthread不对errno这个全局变量进行处理,但是,别忘了pthread_create中的第三个
参数,它指向的函数有可能会调用其它的一些函数,可能会产生错误,将其结构保存到errno中(这些东
西并不矛盾,但是别忘了,在多线程并发的时候,不可能让某一个变量改变总的全局变量,所以线程库就对
每一个新创建的线程都提供一个errno变量)
但是,对于pthread函数的错误,通过读取返回值的判定要比读取全局变量的开销要小的多
也就是说:当我们创建一个线程,第三个参数所指向的线程函数如果还调用了其它线程库的函数,我们是可以通
过返回值来判定的。而对于其它的函数,仍然是返回失败是-1
这样我们来实现一个这样的功能,主线程这边打印A,新创建的线程打印B(通常不称之为:子线程,因为两者没
有父子之称,但是可以把第一个初始的线程称为主线程,新创建的一定不能是子线程)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
运行结果:
可以看到,我们的编译过程加入了(-lpthread),程序中出现了警告(这并不影响我们的程序)
程序的结果如此的合理,
但是,有的时候我们的出现全是A的情况,B没有打印输出的情况,这可能是由于:主线程执行的太快了,已经
结束了,但是新创建的线程还没有被调度到
从上面的结果也可以看到,两者是交替运行的,是在抢占时间片
为了更清楚的看到交替,我们在线程中都假如usleep(20).我们再来看看结果:
可以看到,多次的运行结果不一样,这取决于系统是如何调度线程的
还有一个问题就是,主线程这边需要睡眠,等待新创建的线程运行结束,如果没有等待,就有可能主线程这边
还已经结束,新创建的线程还没执行完毕,所以一般情况下,要让主线程等待一段时间
如果不等待的话:
可以看到上面两者不齐,也就算新的线程还没有执行完成。。。。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
程序如上
为了解决上面的问题,我们选择了在主线程之后加入sleep(1).
那么我们能不能有一个新创建的函数等待新创建的函数结束呢????
所以这里我们可以用:pthread_join来等待新创建的线程的结束
1 2 3 |
|
接收两个参数:
thread:线程ID
retval:是一个无类型指针的指针
同时,这个函数也是成功返回0,错误返回错误代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
pthread_join(等待新创建的线程结束),如果没有这个函数的话,那么是不会进行等待的
类似与进程的waitpid等待其结束。。。。
线程的终止函数pthread_exit(同样的,类似与进程的exit函数)
1 2 3 |
|
参数:retval传的一个参数,如果是空的话,那么pthread_join的第二个参数就算NULL
如果非空的话,那么就将该字符传给pthread_join函数的第二个参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
|
运行结果:
更为准确的说法:应该是新创建的线程的返回值应该为作为pthread_join的第二个参数
如下:当我们把新创建的线程写成这样的时候:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
运行结果:
也就是说:线程的调用可以在任何地方调用pthread_exit,进程的调用页可以在任何地方调用exit
当然,我们还可以在main中调用return表示进程的退出
在调用线程的函数中调用return也可以退出线程
当然:这里还涉及到僵进程,这是什嘛情况吗???
子进程结束了,父进程还没有结束,那么这个时候,子进程会保留一个状态,直到父进程调用wait或者waitpid,那么才不会出现僵进程,这个僵进程的状态才会结束(从子进程结束到父进程调用wait函数期
间)
同样的,也有僵线程的概念:
新创建的线程结束了,但是主线程这边并没有调用pthread_join,那么此刻也就仍然处于僵线程的状态中了。
实际上,我们可以设置线程的属性,来避免僵线程,因为有一些程序,我们不会去调用pthread_join来
避免僵线程的,那么这个时候我们会将线程的属性设置为脱离的(如果:我们一开始没有设置线程的属性为脱离的
,那么我们在过程中可以调用pthread_detach方法,来进行脱离),那么对于脱离的线程就不会产生僵线程
返回当前线程的ID(pthread_self)
1 2 3 |
|
同上,还是成功的时候返回0,错误的时候返回错误码
取消一个执行中的线程(或者杀死一个线程):pthread_cancel
1 2 3 4 |
|
同样类比进程中的杀死,kill
所以说:线程的结束有两种可能:
1,自杀(return,pthread_exit)
2,他杀(pthread_cancel)