线程属性
当我们第一次了解线程时,我们并没有讨论线程属性的问题。我们现在会进行讨论。线程有许多我们可以控制的属性。然而,在这里我们只讨论那些我们最需要的线程属性。其他属性的详细信息可以在手册中了解到。
在所有我们前面的例子中,我们不得不在允许程序退出之前使用pthread_join来重新同步我们的线程。如里我们希望允许一个线程向创建他的线程返回数据时我们需要这样做。有时我们并不需要第二个线程向主线程返回信息,也不希望主线程等待第二个线程。
假设我们创建了第二个线程来备份正在编辑的数据文件的一份拷贝,而主线程继续向用户服务。当备份完成时,第二个线程只是简单的退出,并没有需要与主线程聚合。
我们可以创建类似这样行为的线程。他们被称之为分离线程,而我们可以通过修改线程属性或是通守调用pthread_detach来创建这样的线程。因为在这里我们希望演示线程属性,我们将会使用前一种方法。
我们所需要的最重要的函数就是pthread_attr_init,这个函数可以初始化一个线程属性对象。
#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr);
再一次说时,如果成功则返回0,如果失败则会返回一个错误代码。
同时存在一个销毁函数:pthread_attr_destroy。这个函数的目的就是允许清除属性对象。一旦对象已经被销毁,这个属性对象就不可以被再次使用,直到他被初始化。
当我们有一个已经初始化的线程属性时,有许多我们可以调用的额外函数来设置不同的属性行为。我们在这里列出一些主要的函数,但是我们只会了解其中的两个:
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param
*param);
int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param
*param);
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit);
int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit);
int pthread_attr_setscope(pthread_attr_t *attr, int scope);
int pthread_attr_getscope(const pthread_attr_t *attr, int *scope);
int pthread_attr_setstacksize(pthread_attr_t *attr, int scope);
int pthread_attr_getstacksize(const pthread_attr_t *attr, int *scope);
正如我们所看到的,有许多我们可以使用的属性,但是幸运的是,我们通常并不会使用其中的大多数。
detachstate:这个属性允许我们避免线程重新聚合的需要。与大多数_set函数相似,他以一个指向属性的指针与一个决定所需状态的标记为参数。pthread_attr_setdetachstate函数的两个可能的标记值为PTHREAD_CREATE_JOINABLE与PTHREAD_CRATE_DETACHED。默认情况下,属性值为PTHREAD_CREATE_JOINABLE,从而我们可以允许两个线程重新聚合。如果状态设置为PTHREAD_CRATE_DETACHED,我们不能调用pthread_joi来恢复另一个线程的退出状态。
schedpolicy:这个属性控制线程是如何被调度的。其选项为SCHED_OTHER,SCHED_RR与SCHED_FIFO。默认情况下,属性值为SCHED_OTHER。另两个调度类型只在具有超级用户权限的情况下运行时才可用,因为他们都具有实时调度,但是却有略为不同的行为。SCHED_RR使用循环法调度,而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_THREAD_ATTR_STACKSIZE的实现上被支持。Linux默认情况下使用大堆栈来实现线程,所以这个特性在Linux上通常是丰富的。
试验--设置分离状态属性
作为我们分离线程的例子,thread5.c,我们创建了一个线程属性,将其设置为分离属性,然后使用这个属性来创建线程。现在当子线程完成时,他以正常的方式调用pthread_exit函数。然而,这次原始线程不再等待与他所创建的线程重新聚合。我们使用一个简单的thread_finished标记来允许主线程检测子线程是否已经结束,并且显示出线程仍共享变量。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void *thread_function(void *arg);
char message[] = "Hello World";
int thread_finished = 0;
int main()
{
int res;
pthread_t a_thread;
pthread_attr_t thread_attr;
res = pthread_attr_init(&thread_attr);
if(res != 0)
{
perror("Attribute creation failed");
exit(EXIT_FAILURE);
}
res = pthread_attr_setdetachstate(&thread_attr,PTHREAD_CREATE_DETACHED);
if(res != 0)
{
perror("Setting detached attribute failed");
exit(EXIT_FAILURE);
}
res = pthread_create(&a_thread,&thread_attr,thread_function,(void *)message);
if(res != 0)
{
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
(void)pthread_attr_destroy(&thread_attr);
while(!thread_finished)
{
printf("Waiting for thread to say it's finished.../n");
sleep(1);
}
printf("Othread thread finished,bye!/n");
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg)
{
printf("thread_function is running. Argument was %s/n",(char *)arg);
sleep(4);
printf("Second thread setting finished flag, and exiting now/n");
thread_finished = 1;
pthread_exit(NULL);
}
正如我们所看到的,设置分离状态允许第二个线程独立完成,而不需要主线程来等待第二个线程。
工作原理
两个重要的代码段为:
pthread_attr_t thread_attr;
res = pthread_attr_init(&thread_attr);
if (res != 0) {
perror("Attribute creation failed");
exit(EXIT_FAILURE);
}
这声明了一个线程属性并且进行了初始化,另一段代码为:
res = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
if (res != 0) {
perror("Setting detached attribute failed");
exit(EXIT_FAILURE);
}
这段代码设置属性值为分离状态。
另一个不同的地方就是创建线程,此时传递属性地址,
res = pthread_create(&a_thread, &thread_attr, thread_function, (void
*)message);
并且为了完整性,当我们使用完这个属性之后要销毁这个属性:
pthread_attr_destroy(&thread_attr);