12.6 线程的属性
在第一次介绍创建线程的函数时,我们并未讨论更高级的线程属性问题。现在我们已介绍完了同步线程的主题,可以回头来看这些线程自身的更高级特性了。我们可以控制的线程属性非常多,但在这里我们将只介绍那些你最可能用到的,其他属性的详细资料可以在手册页中找到。
在前面的所有程序示例中,我们都在程序退出之前用pthread_join对线程再次进行同步,如果我们想让线程向创建它的线程返回数据就需要这样做。但有时也会有这种情况,我们既不需要第二个线程向主线程返回信息,也不想让主线程等待它的结束。
假设我们在主线程继续为用户提供服务的同时创建了第二个线程,新线程的作用是将用户正在编辑的数据文件进行备份存储。备份工作结束后,第二个线程就可以直接终止了,它没有必要再回到主线程中。
我们可以创建这一类型的线程,它们被称为脱离线程(detached thread)。可以通过修改线程属性或调用pthread_ detach的方法来创建它们。因为本节的目的是介绍线程的属性,所以在这里我们就使用前一种方法。
需要用到的最重要的函数是pthread_attr_init,它的作用是初始化一个线程属性对象。
与前面的函数一样,它在成功时返回0,失败时返回错误代码。
还有一个回收函数pthread_attr_destroy,它的目的是对属性对象进行清理和回收。一旦对象被回收了,除非它被重新初始化,否则就不能被再次使用。
初始化一个线程属性对象后,我们可以调用许多其他的函数来设置不同的属性行为。我们把其中主要的一些函数列在下面(完整的列表见手册页,通常位于pthread.h条目下),但只对其中的两个(detachedstate和schedpolicy)做详细的介绍:
如你所见,可以使用的线程属性非常多。但幸运的是,你通常不需要设置太多属性就可以让程序正常工作。
❑ detachedstate:这个属性允许我们无需对线程进行重新合并。与大多数_set类函数一样,它以一个属性指针和一个标志为参数来确定需要的状态。pthread_attr_setdetachstate函数可能用到的两个标志分别是PTHREAD_CREATE_JOINABLE和PTHREAD_CREATE_DETACHED。这个属性的默认标志值是PTHREAD_CREATE_JOINABLE,所以可以允许两个线程重新合并。如果标志设置为PTHREAD_CREATE_DETACHED,就不能调用pthread_join来获得另一个线程的退出状态。
❑ schedpolicy:这个属性控制线程的调度方式。它的取值可以是SCHED_OTHER、SCHED_RP和SCHED_FIFO。这个属性的默认值为SCHED_OTHER。另外两种调度方式只能用于以超级用户权限运行的进程,因为它们都具备实时调度的功能,但在行为上略有区别。SCHED_RP使用循环(round-robin)调度机制,而SCHED_FIFO使用“先进先出”策略。
❑ schedparam:这个属性是和schedpolicy属性结合使用的,它可以对以SCHED_OTHER策略运行的线程的调度进行控制。我们将在本章的后面看到一个使用这个属性的例子。
❑ inheritsched:这个属性可取两个值:PTHREAD_EXPLICIT_SCHED和PTHREAD_INHERIT_SCHED。它的默认取值是PTHREAD_EXPLICIT_SCHED,表示调度由属性明确地设置。如果把它设置为PTHREAD_INHERIT_SCHED,新线程将沿用其创建者所使用的参数。
❑ scope:这个属性控制一个线程调度的计算方式。由于目前Linux只支持它的一种取值PTHREAD_SCOPE_SYSTEM,所以在这里我们就不做进一步介绍了。
❑ stacksize:这个属性控制线程创建的栈大小,单位为字节。它属于POSIX规范中的“可选”部分,只有在定义了宏_POSIX_THREAD_ATTR_STACKSIZE的实现版本中才支持。Linux在实现线程时,默认使用的栈很大,所以这个功能对Linux来说显得有些多余。
实 验 设置脱离状态属性
在脱离线程示例thread5.c中,我们创建一个线程属性,将其设置为脱离状态,然后用这个属性创建一个线程。子线程结束时,它照常调用pthread_exit,但这次,原先的线程不再等待与它创建的子线程重新合并。主线程通过一个简单的thread_finished标志来检测子线程是否已经结束,并显示线程之间仍然共享着变量。
输出结果是:
如你所见,设置脱离状态属性可以允许第二个线程独立地完成工作,而无需原先的线程等待它。
实验解析
这个程序中有两段比较重要的代码,第一段代码是:
它声明了一个线程属性并对其进行初始化,第二段代码是:
它把属性的值设置为脱离状态。
其他的细微区别是创建线程和传递属性的地址:
属性用完后,对其进行清理回收:
线程属性——调度
我们来看另外一个可能希望修改的线程属性:调度。改变调度属性和设置脱离状态非常类似,可以用sched_get_priority_max和sched_get_priority_min这两个函数来查找可用的优先级级别。
实 验 调度
因为这里的程序thread6.c与前面的例子很相似,所以我们只显示它与前面例子的不同之处。
(1)首先,定义一些额外的变量:
(2)设置好脱离属性后,设置调度策略:
(3)接下来查找允许的优先级范围:
(4)然后设置优先级:
运行这个程序,它的输出如下所示:
实验解析
这与设置脱离状态属性很相似,区别只是我们设置的是调度策略。