在上一篇的练习里,我们用pthread_create函数创建了一个线程,在这个线程中,我们使用了默认参数,即将该函数的第二个参数设为NULL。的确,对大多数程序来说,使用默认属性就够了,但我们还是有必要来了解一下线程的有关属性。
属性设置
属性结构为pthread_attr_t,它同样在头文件/usr/include/pthread.h中定义
属性值不能直接设置,须使用相关函数进行操作,初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用。比如:
- #include <pthread.h>
- pthread_attr_t attr;
- pthread_t tid;
- /*初始化属性值,均设为默认值*/
- pthread_attr_init(&attr);
- pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
- pthread_create(&tid, &attr, (void *) my_function, NULL);
属性对象主要包括是否绑定、是否分离、堆栈地址、堆栈大小、优先级。默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。
关于线程的绑定,牵涉到另外一个概念:轻进程(LWP:Light Weight Process)。轻进程可以理解为内核线程,它位于用户层和系统层之间。系统对线程资源的分配、对线程的控制是通过轻进程来实现的,一个轻进程可以控制一个或多个线程。默认状况下,启动多少轻进程、哪些轻进程来控制哪些线程是由系统来控制的,这种状况即称为非绑定的。绑定状况下,则顾名思义,即某个线程固定的"绑"在一个轻进程之上。被绑定的线程具有较高的响应速度,这是因为CPU时间片的调度是面向轻进程的,绑定的线程可以保证在需要的时候它总有一个轻进程可用。通过设置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足诸如实时反应之类的要求。
线程的分离状态决定一个线程以什么样的方式来终止自己。线程的默认属性是非分离状态,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。设置线程分离状态的函数为
pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。
第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。
这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。
另外一个可能常用的属性是线程的优先级,它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。
再看常用的几个函数:
1、pthread_attr_init
功能: 对线程属性变量的初始化。
头文件: <pthread.h>
函数原型: int pthread_attr_init (pthread_attr_t* attr);
函数传入值:attr:线程属性。
函数返回值:成功: 0
失败: -1
2、pthread_attr_setscope
功能: 设置线程绑定属性。
头文件: <pthread.h>
函数原型: int pthread_attr_setscope (pthread_attr_t* attr, int scope);
函数传入值:attr: 线程属性。
scope:PTHREAD_SCOPE_SYSTEM(绑定)
PTHREAD_SCOPE_PROCESS(非绑定)
函数返回值得:同1。
3、pthread_attr_setdetachstate
功能: 设置线程分离属性。
头文件: <phread.h>
函数原型: int pthread_attr_setdetachstate (pthread_attr_t* attr, int detachstate);
函数传入值:attr:线程属性。
detachstate:PTHREAD_CREATE_DETACHED(分离)
PTHREAD_CREATE_JOINABLE(非分离)
函数返回值得:同1。
4、pthread_attr_getschedparam
功能: 得到线程优先级。
头文件: <pthread.h>
函数原型: int pthread_attr_getschedparam (pthread_attr_t* attr, struct sched_param* param);
函数传入值:attr:线程属性;
param:线程优先级;
函数返回值:同1。
5、pthread_attr_setschedparam
功能: 设置线程优先级。
头文件: <pthread.h>
函数原型: int pthread_attr_setschedparam (pthread_attr_t* attr, struct sched_param* param);
函数传入值:attr:线程属性。
param:线程优先级。
函数返回值:同1。
然后编写一个程序练习 :
- /*threadtest2.c*/
- #include <stdlib.h>
- #include <stdio.h>
- #include <pthread.h>
- #include <errno.h>
- /*声明线程运行服务程序*/
- static void pthread_func_1 (void);
- static void pthread_func_2 (void);
- int main (void)
- {
- /*线程的标识符*/
- pthread_t pt_1 = 0;
- pthread_t pt_2 = 0;
- int ret = 0;
- pthread_attr_t attr = {0}; //属性结构
- /*属性设置*/
- pthread_attr_init (&attr); //初始化属性
- pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); //绑定
- pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); //分离
- /*分别创建线程1、2*/
- ret = pthread_create (&pt_1, //线程标识符指针
- &attr, //默认属性
- (void *)pthread_func_1,//运行函数
- NULL); //无参数
- if (ret != 0)
- {
- perror ("pthread_1_create");
- }
- ret = pthread_create (&pt_2, //线程标识符指针
- NULL, //默认属性
- (void *)pthread_func_2, //运行函数
- NULL); //无参数
- if (ret != 0)
- {
- perror ("pthread_2_create");
- }
- /*等待线程2的结束*/
- pthread_join (pt_2, NULL);
- sleep(2);//注意线程2结束时线程1没结束,这里不调用sleep会看不到
- printf ("main programme exit!/n");
- return 0;
- }
- /*线程1的服务程序*/
- static void pthread_func_1 (void)
- {
- int i = 0;
- for (; i < 6; i++)
- {
- printf ("This is pthread1!/n");
- /*i==2时退出,即循环3次*/
- if (i == 2)
- {
- pthread_exit (0);
- }
- sleep (1);
- }
- }
- /*线程2的服务程序*/
- static void pthread_func_2 (void)
- {
- int i = 0;
- for (; i < 3; i++)
- {
- printf ("This is pthread2!/n");
- }
- pthread_exit (0);
- }
编译:
$ gcc threadtest2.c -lpthread -o threadtest2
运行:
$ ./threadtest2
好,运行结果如下:
This is pthread1!
This is pthread2!
This is pthread2!
This is pthread2!
This is pthread1!
This is pthread1!
main programme exit!
我们可以看出和上一篇的结果是一样的,不过这次线程一的线程函数一结束就自动释放资源,线程二就得等到pthread_join来释放系统资源