1 一次性初始化
对一些Posix变量,如互斥量,我们可以静态地初始化pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;当不能静态地初始化一个互斥量时,只好动态初始化pthread_mutex_init。这样,问题就来了。在使用多线程时,如果多个线程并发地执行动态初始化时,该互斥量就会被初始化多次,而该过程本该仅仅执行一次。故而,对一些不该被多次初始化的Posix变量,如互斥量、线程键等,执行一次性初始化是最好的选择。
1.1函数
头文件 | #include <pthread.h> |
函数 | int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)); |
参数 | once_control:控制变量,必须静态初始化 pthread_once_t once_control = PTHREAD_ONCE_INIT; init_routine:初始化函数 |
返回 | 成功返回0,出错返回错误码 |
功能 | pthread_once函数首先检查控制变量,以判断是否已经完成初始化。如果完成,pthread_once简单地返回;否则, pthread_once调用初始化函数init_routine,并且记录初始化被完成。如果一个线程初始化时,另外的线程调用Pthread_once,则调用线程将等待,直到那个线程完成初始化后返回。 |
1.2实例(以互斥量为例,线程键例子第4节)
#include <pthread.h>
#include <stdio.h>
pthread_once_t once_control = PTHREAD_ONCE_INIT;/*控制变量*/
pthread_mutex_t mutex;
/*初始化函数*/
void once_init_routine(void)
{
pthread_mutex_init(&mutex, NULL);
}
void *thread_routine(void *arg)
{
int ret;
/*一次初始化互斥量mutex*/
ret = pthread_once(&once_control, once_init_routine);
if (ret != 0)
{
printf("Once init eror: %s\n", strerror(ret));
pthread_exit((void *)0);
}
pthread_mutex_lock(&mutex);
printf("thread routine has locked the mutex\n");
pthread_mutex_unlock(&mutex);
return NULL;
}
int main(int argc, char *argv[])
{
int status;
pthread_t thread_id;
status = pthread_create(&thread_id, NULL, thread_routine, NULL);
if (status != 0)
{
printf("pthread_create eror: %s\n", strerror(status));
pthread_exit((void *)1);
}
status = pthread_once(&once_control, once_init_routine);
if (status != 0)
{
printf("Once init eror: %s\n", strerror(status));
pthread_exit((void *)1);
}
pthread_mutex_lock(&mutex);
printf("main has locked the mutex.\n");
pthread_mutex_unlock(&mutex);
pthread_join(thread_id, NULL);
return 0;
}
2 属性
2.1互斥量属性
POSIX为互斥量定义了3个属性:pshared、protocol和prioceiling,其中pshared属性默认为PTHREAD_PROCESS_PRIVATE,也可设置为PTHREAD_PROCESS_SHARED,这样就能使用互斥量在多个进程间同步线程。
pthread_mutexattr_t attr;/*互斥量属性*/ /*初始化互斥量属性为默认值*/ int pthread_mutexattr_init(pthread_mutexattr_t *attr); /*销毁互斥量属性对象*/ int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); /*设置互斥量共享属性*/ int pthread_mutexattr_setpshard(pthread_mutexattr_t *attr, int pshared); pshared为PTHREAD_PROCESS_PRIVATE或PTHREAD_PROCESS_SHARED /*获取互斥量共享属性*/ int pthread_mutexattr_getpshard(pthread_mutexattr_t *attr, int *pshared); |
#include <pthread.h>
#include <stdio.h>
pthread_mutex_t mutex;
int main(int argc, char *argv[])
{
int ret;
pthread_mutexattr_t mutex_attr;
pthread_mutexattr_init(&mutex_attr);
ret = pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_PRIVATE);
if (ret != 0)
{
printf("mutex setpshared error : %s\n", strerror(ret));
pthread_exit((void *)0);
}
pthread_mutex_init(&mutex, &mutex_attr);
return 0;
}
2.2条件变量属性
POSIX仅为条件变量定义一个属性:pshared。默契为PTHREAD_PROCESS_PRIVATE,当设置为PTHREAD_PROCESS_SHARED时,条件变量能被多个进程中的线程使用。
pthread_condattr_t attr;/*条件变量属性*/ /*初始化条件变量属性为默认值*/ int pthread_condattr_init(pthread_condattr_t *attr); /*销毁条件变量属性对象*/ int pthread_condattr_destroy(pthread_condattr_t *attr); /*设置条件变量共享属性*/ int pthread_condattr_setpshard(pthread_condattr_t *attr, int pshared); pshared为PTHREAD_PROCESS_PRIVATE或PTHREAD_PROCESS_SHARED /*获取条件变量共享属性*/ int pthread_condattr_getpshard(pthread_condattr_t *attr, int *pshared); |
#include <pthread.h>
#include <stdio.h>
pthread_cond_t cond;
int main(int argc, char *argv[])
{
int ret;
pthread_condattr_t cond_attr;
pthread_condattr_init(&cond_attr);
ret = pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_PRIVATE);
if (ret != 0)
{
printf("cond setpshared error : %s\n", strerror(ret));
pthread_exit((void *)0);
}
pthread_cond_init(&cond, &cond_attr);
return 0;
}
2.3线程属性
POSIX为线程创建定义下列属性:detachstate、stacksize、stackaddr、scop等等。detachstate的默认属性为PTHREAD_CREATE_JOINABLE,即创建被创建为可连接的,意味着由pthread_create创建的该线程ID能被用来与该线程连接并获得它的返回值,或者取消它。当detachstate属性被设置为PTHREAD_CREATE_DETACHED时,即创建的线程ID不能被使用,意味着当线程终止时,它使用的任何资源将立刻被系统回收。
pthread_attr_t attr;/*线程属性*/ /*初始化线程属性为默认值*/ int pthread_attr_init(pthread_attr_t *attr); /*销毁线程属性对象*/ int pthread_attr_destroy(pthread_attr_t *attr); /*设置线程分离属性*/ int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); pshared为PTHREAD_CREATE_DETACHED或PTHREAD_CREATE_JOINABLE /*获取线程分离属性*/ int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate); |
#include <pthread.h>
#include <stdio.h>
void *thread_routine(void *arg)
{
printf("The thread is here\n");
return NULL;
}
int main(int argc, char *argv[])
{
int ret;
pthread_t thread_id;
pthread_attr_t thread_attr;
pthread_attr_init(&thread_attr);
ret = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
if (ret != 0)
{
printf("set detach error : %s\n", strerror(ret));
pthread_exit((void *)0);
}
ret = pthread_create(&thread_id, &thread_attr, thread_routine, NULL);
if (ret != 0)
{
printf("create thread error : %s\n", strerror(ret));
pthread_exit((void *)0);
}
printf("Main exiting\n");
pthread_exit(NULL);
return 0;
}
3 取消
当你发现一个线程对完成事情不再是必要时,你可以取消它。除非你确实想要目标线程消失,否则应该尽量避免使用取消操作。
调用pthread_cancel(thread_id)时,它提出取消线程thread_id的请求,线程thread_id在取消请求发出后仍会继续执行,直到到达某个取消点。取消点是线程检查是否被取消并按照请求进行动作的一个位置。
3.1函数
头文件 | #include <pthread.h> |
函数 | int pthread_cancel(pthread_t thread); |
参数 | thread:线程ID |
返回 | 成功返回0,出错返回错误码 |
功能 | 发出取消线程thread的请求 |
头文件 | #include <pthread.h> |
函数 | int pthread_setcancelstate(int state, int *oldstate); |
参数 | state:待设置的状态 oldstate:原先的状态 |
返回 | 成功返回0,出错返回错误码 |
功能 | 设置线程取消状态:启用(PTHREAD_CANCEL_ENABLE)或禁用(PTHREAD_CANCEL_DISABLE) |
头文件 | #include <pthread.h> |
函数 | int pthread_setcanceltype(int type, int *oldtype); |
参数 | type:待设置的类型 oldtype:原先的类型 |
返回 | 成功返回0,出错返回错误码 |
功能 | 设置线程取消类型:推迟(PTHREAD_CANCEL_DELIVEROD)或异步(PTHREAD_CANCEL_ASYNCHRONOUS) |
头文件 | #include <pthread.h> |
函数 | void pthread_testcancel(void); |
参数 | void |
返回 | 成功返回0,出错返回错误码 |
功能 | 创建一个取消点 |
3.2取消点
线程取消的方法是向目标线程发CANCELED信号,但如何处理Cancel信号则由目标线程自己决定,或者忽略、或者立即终止、或者继续运行至Cancelation-point(取消点),由不同的Cancelation状态决定。
线程接收到CANCEL信号的缺省处理(即pthread_create()创建线程的缺省状态)是继续运行至取消点,也就是说设置一个CANCELED状态,线程继续运行,只有运行至Cancelation-point的时候才会退出。
pthreads标准指定了几个取消点,其中包括:
(1)通过pthread_testcancel调用以编程方式建立线程取消点。
(2)线程等待pthread_cond_wait或pthread_cond_timewait()中的特定条件。
(3)被sigwait(2)阻塞的函数
(4)一些标准的库调用。通常,这些调用包括线程可基于阻塞的函数。
3.3 实例
#include <stdio.h>
#include <pthread.h>
static int counter;
void *thread_routine(void *arg)
{
for (counter = 0; ;counter++)
{
if ((counter % 1000 ) == 0)
{
pthread_testcancel();/*创建取消点*/
}
}
}
int main(int argc, char *argv[])
{
pthread_t thread_id;
void *result;
int status;
status = pthread_create(&thread_id, NULL, thread_routine, NULL);
if (status != 0)
{
printf("create thread error: %s\n", strerror(status));
pthread_exit((void *)0);
}
sleep(2);
status = pthread_cancel(thread_id);/*发出取消请求*/
if (status != 0)
{
printf("cancel thread error: %s\n", strerror(status));
pthread_exit((void *)0);
}
status = pthread_join(thread_id , &result);
if (status != 0)
{
printf("join thread error: %s\n", strerror(status));
pthread_exit((void *)0);
}
if (result == PTHREAD_CANCELED)
{
printf("thrad canceled at iteration %d\n", counter);
}
else
{
printf("thread was not canceled\n");
}
return 0;
}
4 线程私有数据
在进程内的所有线程共享相同的地址空间,即意味着任何声明为静态或外部的变量,或在进程堆声明的变量,都可以被进程内所有线程读写。一个线程真正拥有的唯一私有存储是处理器寄存器,甚至栈地址也能被共享。
程序创建一个键,然后每个线程就能独立地设定或得到自己的键值。键对于所有的线程是相同的,但是每个线程能将它独立的键值与共享键联系。每个线程能在任何时间为键改变它的私有值,而不会影响键或任何另外线程拥有的键值。
4.1函数
pthread_key_t key;/*线程键*/ /*创建线程键*/ int pthread_key_create(pthread_key_t *key, void (*destrutor)(void *)); /*销毁线程键*/ int pthread_key_delete(pthread_key_t key); /*设置线程私有数据*/ int pthread_setspecific(pthread_key_t key, const void *value); /*获取线程私有数据*/ void *pthread_getspecific(pthread_key_t key); |
4.2实例
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
typedef struct tsd_tag
{
pthread_t thread_id;
char *string;
}tsd_t;
pthread_key_t tsd_key;/*线程键*/
pthread_once_t key_once;/*控制变量*/
void once_routine(void)
{
int ret;
printf("Initializing key...\n");
ret = pthread_key_create(&tsd_key, NULL);
if (ret != 0)
{
printf("create key error: %s\n", strerror(ret));
pthread_exit((void *)0);
}
}
void *thread_routine(void *arg)
{
int ret;
tsd_t *value;
ret = pthread_once(&key_once, once_routine);
if (ret != 0)
{
printf("pthread once error: %s\n", strerror(ret));
pthread_exit((void *)0);
}
value = (tsd_t *)malloc(sizeof(tsd_t));
if (value == NULL)
{
printf("malloc error");
pthread_exit((void *)0);
}
/*设置线程私有数据*/
ret = pthread_setspecific(tsd_key, value);
if (ret != 0)
{
printf("pthread_setspecific error: %s\n", strerror(ret));
pthread_exit((void *)0);
}
printf("%s set tsd value %p\n", arg, value);
value->thread_id = pthread_self();
value->string = (char *)arg;
/*获取线程私有数据*/
value = (tsd_t *)pthread_getspecific(tsd_key);
printf("%s starting ...\n", value->string);
sleep(2);
value = (tsd_t *)pthread_getspecific(tsd_key);
printf("%s done...\n", value->string);
return NULL;
}
int main(int argc, char *argv[])
{
int ret;
pthread_t thread1, thread2;
ret = pthread_create(&thread1, NULL, thread_routine, "thread 1");
if (ret != 0)
{
printf("create thread 1 error: %s\n", strerror(ret));
pthread_exit((void *)0);
}
ret = pthread_create(&thread2, NULL, thread_routine, "thread 2");
if (ret != 0)
{
printf("create thread 2 error: %s\n", strerror(ret));
pthread_exit((void *)0);
}
pthread_exit(NULL);
}