1线程属性
创建线程pthread_create的参数pthread_attr_tattr;可以用来定制各种不同的线程属性。
detachstate:线程的分离状态属性
guardsize:线程栈末尾的警戒缓冲区大小
stackaddr:线程栈的最低地址
stacksize:线程栈的大小
1.1线程属性的初始化和销毁
#include <pthread.h> int pthread_attr_init(pthread_attr_t *attr); /*将attr初始化为系统支持的默认属性值*/ int pthread_attr_destroy(pthread_attr_t *arrt);/*释放空间,无效值初始化attr*/ 返回值:若成功返回0,否则返回错误编号 |
1.2线程的分离状态属性
#include <pthread.h> int pthread_attr_getdetachstate(const pthread_attr_t *restrict attr, int *detachstate); int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); 返回值:若成功返回0,否则返回错误编号 |
如果创建线程时就知道不需要了解线程的终止状态,可以修改pthread_attr_t结构中的detachstate线程属性,让线程以分离状态启动。
分离状态属性detachstate有两个合法值:PTHREAD_CREATE_DETACHED,以分离状态启动线程;PTHREAD_CREATE_JOINABLE,正常启动线程,应用程序可以获取线程的终止状态。
1.3线程的缓冲区大小
#include <pthread.h> int pthread_attr_getguardsize (const pthread_attr_t *restrict attr, size_t *restrict guardsize); int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize); 返回值:若成功返回0,否则返回错误编号 |
线程缓冲区大小属性guardsize控制着线程栈末尾之后用以避免栈溢出的扩展内存的大小。该属性默认值为PAGESIZE个字节。也可以把guardsize线程属性设为0,从而不允许属性的这种行为发生:在这种情况下不会提供警戒缓冲区。
1.4线程栈的最低地址
#include <pthread.h> int pthread_attr_getstack (const pthread_attr_t *restrict attr, void **restrict stackaddr, size_t *restrict stacksize); int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize); 返回值:若成功返回0,否则返回错误编号 |
这两个函数可以用于管理stackaddr线程属性,也可以用于管理stacksize线程属性。
1.5线程栈的大小
#include <pthread.h> int pthread_attr_getstacksize (const pthread_attr_t *restrict attr, size_t *restrict stacksize); int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); 返回值:若成功返回0,否则返回错误编号 |
这两个函数用来读取或设置线程属性stacksize。
2同步属性
2.1互斥量属性
#include <pthread.h> int pthread_mutexattr_init(pthread_mutexattr_t *attr); /*初始化互斥量属性*/ int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); /*回收互斥量属性*/ 返回值:若成功则返回0,否则返回错误编号 |
2.2读写锁属性
#include <pthread.h> int pthread_rwlockattr_init(pthread_rwlockattr_t *attr); /*初始化读写锁属性*/ int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr); /*回收读写锁属性*/ 返回值:若成功则返回0,否则返回错误编号 |
2.3条件变量属性
#include <pthread.h> int pthread_condattr_init(pthread_condattr_t *attr); /*初始化条件变量属性*/ int pthread_condattr_destroy(pthread_condattr_t *attr); /*回收条件变量属性*/ 返回值:若成功则返回0,否则返回错误编号 |
3重入
可重入与不可重入:(转载)
这种情况出现在多任务系统当中,在任务执行期间捕捉到信号并对其进行处理时,进程正在执行的指令序列就被信号处理程序临时中断。如果从信号处理程序返回,则继续执行进程断点处的正常指令序列,从重新恢复到断点重新执行的过程中,函数所依赖的环境没有发生改变,就说这个函数是可重入的,反之就是不可重入的。
众所周知,在进程中断期间,系统会保存和恢复进程的上下文,然而恢复的上下文仅限于返回地址,cpu寄存器等之类的少量上下文,而函数内部使用的诸如全局或静态变量,buffer等并不在保护之列,所以如果这些值在函数被中断期间发生了改变,那么当函数回到断点继续执行时,其结果就不可预料了。打个比方,比如malloc,将如一个进程此时正在执行malloc分配堆空间,此时程序捕捉到信号发生中断,执行信号处理程序中恰好也有一个malloc,这样就会对进程的环境造成破坏,因为malloc通常为它所分配的存储区维护一个链接表,插入执行信号处理函数时,进程可能正在对这张表进行操作,而信号处理函数的调用刚好覆盖了进程的操作,造成错误。
满足下面条件之一的多数是不可重入函数:
(1)使用了静态数据结构;
(2)调用了malloc或free;
(3)调用了标准I/O函数;标准io库很多实现都以不可重入的方式使用全局数据结构。
(4)进行了浮点运算.许多的处理器/编译器中,浮点一般都是不可重入的 (浮点运算大多使用协处理器或者软件模拟来实现。
如果一个函数在同一时刻可以被多个线程安全地调用,就称该函数是线程安全的。
如果一个函数对多个线程来说是可重入的,则说这个函数是线程安全的,但这并不能说明对信号处理程序来说该函数也是可重入的。
如果函数对异步信号处理程序的重入是安全的,那么就可以说函数是异步-信号安全的。
(转载)
可重入函数,与多线程无关,即可重入概念并不依赖于多线程,可重入的提出是依据单一线程提出来的,当然,多线程可重入是它的扩展。一个函数被同一个线程调用2次以上,得到的结果具有可再现性(多次调用函数,得到的结果是一样的)。那么我们说这个函数是可重入的。
为了保证函数是可重入的,需要做到一下几点:
1,不在函数内部使用静态或者全局数据
2,不返回静态或者全局数据,所有的数据都由函数调用者提供
3,使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据
4,如果必须访问全局数据,使用互斥锁来保护
5,不调用不可重入函数
线程安全函数,如果一个函数能够安全的同时被多个线程调用而得到正确的结果,那么,我们说这个函数是线程安全的。所谓安全,一切可能导致结果不正确的因素都是不安全的调用。
线程安全,是针对多线程而言的。那么和可重入联系起来,我们可以断定,可重入函数必定是线程安全的,但是线程安全的,不一定是可重入的。不可重入函数,函数调用结果不具有可再现性,可以通过互斥锁等机制,使之能安全的同时被多个线程调用,那么,这个不可重入函数就是转换成了线程安全。
4线程私有数据
线程私有数据(也称线程特定数据)是存储和查询与某个线程相关的数据的一种机制。
1在分配线程私有数据之前,需要创建与该数据关联的键:
#include <pthread.h> int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void)); 返回值:成功返回0,失败返回错误编号 |
2消除键与线程私有数据值之间的关联关系
#include <pthread.h> int pthread_key_delete(pthread_key_t *key); 返回值:成功返回0,失败返回错误编号 |
3确保分配的键并不会由于在初始化阶段的竞争而发生变动
#include <pthread.h> pthread_once_t initflag = PTHREAD_ONCE_INIT; int pthread_once(pthread_once_t *initflag, void (*initfn)(void)); 返回值:成功返回0,失败返回错误编号 |
5取消选项
有两个线程属性并没有包含在pthread_attr_t结构中,它们是可取消状态和可取消类型。这两个属性影响着线程在响应pthread_cancel函数调用时所呈现的行为。
6线程和信号
1线程中阻止信号发生
#include <pthread.h> int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict oset); 返回值:成功返回0,失败返回错误编号 |
2线程等待一个或多个信号发生
#include <pthread.h> int sigwait(const sigset_t *restrict set, int *restrict signop); 返回值:成功返回0,失败返回错误编号 |
3把信号发送到线程
#include <pthread.h> int pthread_kill(pthread_t thread, int signo); 返回值:成功返回0,失败返回错误编号 |
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
int quitflag;
sigset_t mask;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t wait = PTHREAD_COND_INITIALIZER;
void *thr_fn(void *arg)
{
int err, signo;
for (;;)
{
err = sigwait(&mask, &signo);
if (err != 0)
{
printf("sigwait failed..\n");
exit(0);
}
switch(signo)
{
case SIGINT:
printf("\ninterrupt\n");
break;
case SIGQUIT:
pthread_mutex_lock(&lock);
printf("\nquit\n");
quitflag = 1;
pthread_mutex_unlock(&lock);
pthread_cond_signal(&wait);
return 0;
default:
printf("unexpeced signal %d\n", signo);
exit(1);
}
}
}
int main(void)
{
int err;
sigset_t oldmask;
pthread_t tid;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGQUIT);
if ((err = pthread_sigmask(SIG_BLOCK, &mask, &oldmask)) != 0)
{
printf("SIG_BLOCK error\n");
exit(1);
}
err = pthread_create(&tid, NULL, thr_fn, 0);
if (err != 0)
{
printf("can't create thread\n");
exit(1);
}
pthread_mutex_lock(&lock);
while (quitflag == 0)
pthread_cond_wait(&wait, &lock);
pthread_mutex_unlock(&lock);
quitflag = 0;
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
{
printf("SIG_SETMASK error\n");
exit(1);
}
return 0;
}
[root]# ./a.out
interrupt
interrupt
interrupt
quit
[root]#