15、线程控制

来源:互联网 发布:mysql的update语句 编辑:程序博客网 时间:2024/06/04 00:49

1、属性对象对应用程序来说是不透明的。这意味着应用程序并不需要了解相关属性对象的内部结构的详细细节,这增加了应用程序的可移植性。取而代之的是,需要提供相应的函数来管理这些属性对象。

2、线程属性

int pthread_attr_init(pthread_attr_t *attr);int pthread_attr_destroy(pthread_attr_t *attr);//线程状态:分离还是可获取终止状态int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);int pthread_attr_setdetachstate(pthread_attr_t *attr, int *detachstate);//如果线程栈地址空间用完了,可以使用malloc和mmap来为可替代的栈分配空间,并用pthread_attr_setstacksize函数来改变新建线程的栈位置。//stackaddr线程属性被定义为栈的最低内存地址,如果栈是从高地址向低地址增长,那么stackaddr线程属性将是栈的结尾位置,而不是开始位置。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);

3、互斥量属性

//属性包括:进程共享属性、健壮属性以及类型属性int pthread_mutexattr_init(pthread_mutexattr_t *attr);int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr, int *restrict pshared);int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);

当持有互斥量的进程终止时,其他线程想要获取锁都会阻塞。使健壮属性为PTHREAD_MUTEX_ROBUST,则线程请求该锁时会返回EOWNERDEAD

int pthread_mutexattr_getrobust(const pthread_mutexattr_t *attr, int *restrict robust);int pthread_mutexattr_setrobust(pthread_mutexattr_t *attr, int robust);//使之前出现问题的互斥量依然可以用int pthread_mutex_consistent(pthread_mutexattr_t *attr);

4、读写锁、条件变量属性、屏障属性类似

5、重入
xx_r以_r结尾的函数是原函数可安全重入版本。如char *getenv(const char *name);和int getenv_r(const char *name, char *buf, int buflen);使getenv_r可重入,需要改变接口,调用者必须提供自己的缓冲区,这样每个线程使用各自不同的缓冲区避免其他线程的干扰。但是还不够,在搜索请求的字符串时保护环境不被修改,可以使用互斥量。

6、线程特定数据
一个进程中所有线程都可以访问这个进程的整个地址空间。虽然底层的实现部分并不能阻止这种访问能力,但是管理线程特定数据的函数可以提高线程间数据的独立性,使得线程不太容易访问到其他线程的特定数据。

//创建键,以及指定与该键关联的析构函数int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *));//得到与键关联的数据地址void *pthread_getspecific(pthread_key_t key);//关联键和数据地址int pthread_setspecific(pthread_key_t key, const void *value);//取消键与数据之间的关联int pthread_key_delete(pthread_key_t *key);
#define MAXSTRINGSZ 4096static pthread_key_t key;static pthread_once_t init_done = PTHREAD_ONCE INIT;pthread_mutex_t env_mutex = PTHREAD_MUTEX_INITIALIZER;extern char **environ;static void thread_init(void){    pthread_key_create(&key, free);}char *getenv(const char *name){    int i, len;    char *envbuf;//通过pthread_once_t init_done参数使thread_init只被线程调用一次    pthread_once(&init_done, thread_init);    pthread_mutex_lock(&env_mutex);    envbuf=(char *)pthread_getspecific(key);    if(envbuf==NULL){        envbuf = malloc(MAXSTRINGSZ);        if(envbuf==NULL){            pthread_mutex_unlock(&env_mutex);            return(NULL);        }        pthread_setspecific(key,envbuf);    }    len = strlen(name);    for(i=0;environ[i]!=NULL;i++)    {        if((strncmp(name, envrion[i]), len) == 0) && (environ[i][len] == '='){            strcpy(envbuf, &environ[i][len+1], MAXSTRINGSZ-1);                                             pthread_mutex_unlock(&env_mutex);        return(envbuf);        }    }    pthread_mutex_unlock(&env_mutex);       return(NULL);}

7、取消选项
调用pthread_cancel并不等待线程终止,而是直到线程到达某个取消点,如accpet等函数。

//设置是否可以取消线程int pthread_setcancelstate(int state, int *oldstate);//增加自己的取消点void pthread_testcancel(void);//设置可以同步取消,异步取消(可以在任何时刻取消,而不是要遇到取消点)int pthread_setcanceltype(int type, int *oldtype);

8、线程和信号
每个线程都有自己的信号屏蔽字,进程中的信号会传递给单个线程。为了防止信号中断线程,可以把信号加到每个线程的屏蔽字中,然后安排专门的线程处理信号。

//sigprocmask的行为在多线程的进程中没有定义,线程使用pthread_sigmaskint pthread_sigmask(int how, const sigset_t *restrict set, sig_set_t *restrict oset);//等待信号出现//set指定等待的信号集,signop包含发送的信号//为了避免错误发送,调用之前,必须阻塞那些线程正在等待的信号//sigwait返回之前将恢复线程的屏蔽字int sigwait(const sigset_t *restrict set, int *restrict signop);//把信号发个线程int pthread_kill(pthread_t thread, int signo);
#include "apue.h"#include <pthread.h>int quitflag;sigset_t mask;pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;pthread_cond_t waitloc = PTHREAD_COND_INITIALIZER;void *thr_fn(void *arg){    int err, signo;    for(;;){        err = sigwait(&mask, &signo);        if(err!=0)            err_exit(err, "sigwait failed");        switch(signo){        case SIGINT:            printf("\ninterrupt\n");            break;        case SIQUIT:            pthread_mutex_lock(&lock);            quitflag = 1;            pthread_mutex_unlock(&lock);            pthread_cond_signal(&waitloc);//通知条件已经满足            return(0);        default:            printf("unexpected 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);    pthread_sigmask(SIG_BLOCK, &mask, &oldmask);    pthread_create(&tid, NULL, thr_fn, 0);    pthread_mutex_lock(&lock);    while(quitflag==0)        pthread_cond_wait(&waitloc, &lock);    pthread_mutex_unlock(&lock);    quitflag = 0;    pthread_sigmask(SIG_SETMASK, &oldmask, NULL);    exit(0);}

9、线程和fork
子进程通过继承整个地址空间的副本,还从父进程哪儿继承了每个互斥量、读写锁和条件变量的状态。父进程包含一个以上线程,在fork之后子进程如果不立马调用exec的话,就需要清理锁状态。

//fork之前安装3个清理函数,prepare在fork之前清理,child在子进程中清理(pid ==0),parent在父进程中清理(pid>0)int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));
原创粉丝点击