UNIX环境高级编程(第12章 线程控制)

来源:互联网 发布:iphone移动数据设置 编辑:程序博客网 时间:2024/06/05 09:08

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)
调用了mallocfree;
(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 interruptinterruptinterruptquit[root]# 



0 0