UNIX/Linux-线程控制(实例入门篇)
2014-01-02 23:53
351 查看
UNIX线程控制
线程属性
在创建线程时,可以用pthread_attr_t结构修改线程默认属性,并把这些属性与创建的线程联系起来。可以用pthread_attr_init函数初始化pthread_attr_t结构。
#include <pthread.h>
int pthread_attr_init (pthread_attr_t* attr) ;
int pthread_attr_destroy (pthread_attr_t* attr) ;
线程属性包括:线程的分离状态属性、线程栈末尾的警戒缓冲区大小、线程栈的最低地址、线程栈的大小……
分离线程
如果对现有的某个线程的终止状态不感兴趣,可以使用pthread_detach函数让操作系统在线程退出时回收它所占用的资源。(分离:不与其他线程联系,即不把自己的终止状态返回给线程)
如果线程已经处于分离状态,pthread_join调用就会失败,返回EINVAL。
如果在创建线程时就知道不需要了解线程的终止状态,则可以修改pthread_attr_t结构的相关属性,可以使用pthread_attr_setdetachstate函数把线程属性detachstate设置为:PTHREAD_CREATE_DETACHED,以分离状态启动。或设置为PTHREAD_CREATE_JOINABLE,正常启动线程,应用程序就可以获取线程的终止状态。
线程私有数据
有时候我们可能会有这种需求:需要一个在线程内的全局变量,即此变量只能让此线程使用,其他线程不能访问。(线程会调用多个模块,各模块之间可能需要一个全局变量)
(当然,我们也可以在线程函数中定义变量,把其地址传给各个子模块函数,这样做毕竟不方便,使函数的接口变的复杂)
在分配线程私有数据之前,需要创建与该数据关联的键。这个键将用于获取对线程私有数据的访问权。
#include <pthread.h>
int pthread_key_create(pthread_key_t * keyp, void (*destructor)(void *)) ;
创建的键存放在keyp指向的内存单元,除创建键以外,pthread_key_create可以选择为该键关联析构函数,当线程调用pthread_exit或线程执行返回,正常退出时,析构函数就会被调用。但如果线程调用了exit、_exit、_Exit、abort或出现其他非正常的退出时,就不会调用析构函数。
线程通常使用malloc为线程私有数据分配内存空间,析构函数通常释放已分配的内存。
键一旦创建,就可以通过调用pthread_setspecific函数把键和线程私有数据关联起来。可以通过pthread_getspecific函数获得线程私有数据的地址。
#include <pthread.h>
int pthread_setspecific(pthread_key_t key, const void * value) ;
void* pthread_getspecific(pthread_key_t key) ;
如果没有线程私有数据值与键关联,pthread_getspecific将返回一个空指针,可以据此来确定是否需要调用pthread_setspecific
【示例】
线程和信号
进程中的信号是递送到单个线程的,如果信号与硬件故障或计时器超时相关,该信号就被发送到引起该事件的线程中去,而其他的信号则被发送到任意一个线程。
每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有线程共享的。
在多线程环境下设置信号屏蔽字:使用pthread_sigmask函数(因为sigprocmask的行为在多线程的进程中没有定义)
#include <signal.h>
int pthread_sigmask (int how, const sigset_t * set, sigset_t* oset) ;
此函数与sigprocmask函数基本相同。
线程等待信号
#include <signal.h>
int sigwait (const sigset_t * set, int * signop) ;
参数set指出了线程等待的信号集,参数signop为返回值是等到的是哪个信号
注意:
为了避免错误动作发生,线程在调用sigwait之前,必须阻塞那些它正在等待的信号。sigwait函数会自动取消信号集的阻塞状态,直到有新的信号被递送。在返回之前,sigwait将恢复线程的信号屏蔽字。
使用sigwait的好处在于它可以简化信号处理。
为了防止信号中断线程,可以把信号加到每个线程的信号屏蔽字中,然后安排专用线程做信号处理。因为sigwait会解除信号的阻塞状态,所以只有一个线程可以用于信号的接收。这使得对主线程进行编码时不必担心来自这些信号的中断。
【注意】闹钟定时器是进程资源,并且所有的线程共享相同的alarm。所以进程中的多个线程不可能互不干扰地使用闹钟定时器。
【示例】
向线程发送信号
要把信号发送到进程,可以调用kill。要把信号发送到线程,可以调用pthread_kill。
#include <signal.h>
int pthread_kill (pthread_t tid, int signo) ;
【示例】
【注意】很多linux系统在编译时,没有自动链接线程库,需要在编译时指定链接:例 gcc a.c -l pthread
线程属性
在创建线程时,可以用pthread_attr_t结构修改线程默认属性,并把这些属性与创建的线程联系起来。可以用pthread_attr_init函数初始化pthread_attr_t结构。
#include <pthread.h>
int pthread_attr_init (pthread_attr_t* attr) ;
int pthread_attr_destroy (pthread_attr_t* attr) ;
线程属性包括:线程的分离状态属性、线程栈末尾的警戒缓冲区大小、线程栈的最低地址、线程栈的大小……
分离线程
如果对现有的某个线程的终止状态不感兴趣,可以使用pthread_detach函数让操作系统在线程退出时回收它所占用的资源。(分离:不与其他线程联系,即不把自己的终止状态返回给线程)
如果线程已经处于分离状态,pthread_join调用就会失败,返回EINVAL。
如果在创建线程时就知道不需要了解线程的终止状态,则可以修改pthread_attr_t结构的相关属性,可以使用pthread_attr_setdetachstate函数把线程属性detachstate设置为:PTHREAD_CREATE_DETACHED,以分离状态启动。或设置为PTHREAD_CREATE_JOINABLE,正常启动线程,应用程序就可以获取线程的终止状态。
线程私有数据
有时候我们可能会有这种需求:需要一个在线程内的全局变量,即此变量只能让此线程使用,其他线程不能访问。(线程会调用多个模块,各模块之间可能需要一个全局变量)
(当然,我们也可以在线程函数中定义变量,把其地址传给各个子模块函数,这样做毕竟不方便,使函数的接口变的复杂)
在分配线程私有数据之前,需要创建与该数据关联的键。这个键将用于获取对线程私有数据的访问权。
#include <pthread.h>
int pthread_key_create(pthread_key_t * keyp, void (*destructor)(void *)) ;
创建的键存放在keyp指向的内存单元,除创建键以外,pthread_key_create可以选择为该键关联析构函数,当线程调用pthread_exit或线程执行返回,正常退出时,析构函数就会被调用。但如果线程调用了exit、_exit、_Exit、abort或出现其他非正常的退出时,就不会调用析构函数。
线程通常使用malloc为线程私有数据分配内存空间,析构函数通常释放已分配的内存。
键一旦创建,就可以通过调用pthread_setspecific函数把键和线程私有数据关联起来。可以通过pthread_getspecific函数获得线程私有数据的地址。
#include <pthread.h>
int pthread_setspecific(pthread_key_t key, const void * value) ;
void* pthread_getspecific(pthread_key_t key) ;
如果没有线程私有数据值与键关联,pthread_getspecific将返回一个空指针,可以据此来确定是否需要调用pthread_setspecific
【示例】
//线程私有数据的例子 // // #include <pthread.h> #include <stdio.h> #include <errno.h> #include <stdlib.h> //线程私有数据的键 对所有线程是可见的(但只有创建它的线程是可以访问的) static pthread_key_t key ; //以下两个条件变量用于线程间的同步 struct { pthread_cond_t cond ; pthread_mutex_t mutex ; int nready ; //允许线程继续执行 } condForThread1 = { PTHREAD_COND_INITIALIZER , PTHREAD_MUTEX_INITIALIZER } ; struct { pthread_cond_t cond ; pthread_mutex_t mutex ; int nready ; //允许线程继续执行 } condForThread2 = { PTHREAD_COND_INITIALIZER , PTHREAD_MUTEX_INITIALIZER } ; void* thread1(void* arg); void* thread2(void* arg); void childfunc(); int main(void) { pthread_t tid1, tid2 ; pthread_create(&tid1, NULL, thread1, NULL) ; pthread_create(&tid2, NULL, thread2, NULL) ; pthread_join(tid1, NULL) ; pthread_join(tid2, NULL) ; return 0 ; } void* thread1(void* arg) { char* privateBuf = NULL ; int bufsize = 10 ; int i = 0 ; pthread_key_create(&key, free) ; //创建键 //获取键的关联数据的指针(检查此键是否已经关联了数据) privateBuf = (char*)pthread_getspecific(key) ; if (privateBuf == NULL) { privateBuf = malloc(bufsize) ; if (privateBuf == NULL) return ; } //设置 键与数据的关联 pthread_setspecific(key, privateBuf) ; childfunc() ; //子模块访问线程的私有数据 //打印buf for (i = 0; i < bufsize; ++i) { printf("%d,\n", privateBuf[i]) ; } //向线程2发信号 唤醒线程2 condForThread2.nready = 1 ; pthread_cond_signal(&condForThread2.cond) ; //挂起 等待来自线程1的信号 pthread_mutex_lock(&condForThread1.mutex) ; pthread_cond_wait(&condForThread1.cond, &condForThread1.mutex) ; pthread_mutex_unlock(&condForThread1.mutex) ; } void childfunc() { int i = 0 ; char* buf ; buf = (char*)pthread_getspecific(key) ; for (i = 0; i < 10; ++i) { buf[i] = i ; } } void* thread2(void* arg) { char* privateBuf = NULL ; int bufsize = 10 ; int i = 0 ; //等待来自线程1的信号 pthread_mutex_lock(&condForThread2.mutex) ; pthread_cond_wait(&condForThread2.cond, &condForThread2.mutex) ; pthread_mutex_unlock(&condForThread2.mutex) ; //获取键的关联数据的指针(会引发段错误,不允许其他线程访问key) privateBuf = (char*)pthread_getspecific(key) ; //打印buf for (i = 0; i < bufsize; ++i) { printf("*%d,\n", privateBuf[i]) ; } //向线程1发信号 condForThread1.nready = 1 ; pthread_cond_signal(&condForThread1.cond) ; }
线程和信号
进程中的信号是递送到单个线程的,如果信号与硬件故障或计时器超时相关,该信号就被发送到引起该事件的线程中去,而其他的信号则被发送到任意一个线程。
每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有线程共享的。
在多线程环境下设置信号屏蔽字:使用pthread_sigmask函数(因为sigprocmask的行为在多线程的进程中没有定义)
#include <signal.h>
int pthread_sigmask (int how, const sigset_t * set, sigset_t* oset) ;
此函数与sigprocmask函数基本相同。
线程等待信号
#include <signal.h>
int sigwait (const sigset_t * set, int * signop) ;
参数set指出了线程等待的信号集,参数signop为返回值是等到的是哪个信号
注意:
为了避免错误动作发生,线程在调用sigwait之前,必须阻塞那些它正在等待的信号。sigwait函数会自动取消信号集的阻塞状态,直到有新的信号被递送。在返回之前,sigwait将恢复线程的信号屏蔽字。
使用sigwait的好处在于它可以简化信号处理。
为了防止信号中断线程,可以把信号加到每个线程的信号屏蔽字中,然后安排专用线程做信号处理。因为sigwait会解除信号的阻塞状态,所以只有一个线程可以用于信号的接收。这使得对主线程进行编码时不必担心来自这些信号的中断。
【注意】闹钟定时器是进程资源,并且所有的线程共享相同的alarm。所以进程中的多个线程不可能互不干扰地使用闹钟定时器。
【示例】
//线程对信号的处理 //SIGQUIT 对应键盘 ctrl+\ //SIGINT 对应键盘 ctrl+c #include <pthread.h> #include <signal.h> #include <stdio.h> #include <errno.h> #include <stdlib.h> sigset_t mask ; //信号集 int quitflag ; //收到退出信号的标记 pthread_cond_t waitcond = PTHREAD_COND_INITIALIZER ; pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER ; void* thr_signal_handle(void* arg) ; 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) perror("SIG_BLOCK error!\n") ; //创建线程 if ((err = pthread_create(&tid, NULL, thr_signal_handle, NULL)) != 0) perror("can't create thread!\n") ; //等待线程返回消息 pthread_mutex_lock(&lock) ; while (quitflag == 0) pthread_cond_wait(&waitcond, &lock) ; pthread_mutex_unlock(&lock) ; quitflag = 0 ; //恢复原进程信号屏蔽字 if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) perror("SIG_SETMASK error!\n") ; exit(0) ; } //专门处理信号的线程 void* thr_signal_handle(void* arg) { int err, signo ; for(;;) { if ((err = sigwait(&mask, &signo)) != 0) perror("sigwait error!\n") ; switch(signo) { case SIGINT: printf("\ninterrupt\n") ; break ; case SIGQUIT: //向主线程发送通知 pthread_mutex_lock(&lock) ; quitflag = 1 ; pthread_mutex_unlock(&lock) ; pthread_cond_signal(&waitcond) ; return ; default: printf("unexpected signal %d\n", signo) ; exit(1) ; } }//for(;;) }
向线程发送信号
要把信号发送到进程,可以调用kill。要把信号发送到线程,可以调用pthread_kill。
#include <signal.h>
int pthread_kill (pthread_t tid, int signo) ;
【示例】
//线程间信号的发送 #include <pthread.h> #include <signal.h> #include <stdio.h> #include <errno.h> #include <stdlib.h> sigset_t mask ; //信号集 pthread_t tidbuf[5] ; //存储进程中所有线程的ID int flag ; pthread_cond_t waitcond = PTHREAD_COND_INITIALIZER ; pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER ; void* thr1(void* arg) ; void* thr2(void* arg) ; int main(void) { int err ; sigset_t oldmask ; pthread_t tid ; sigemptyset(&mask) ; sigaddset(&mask, SIGUSR1) ; //阻塞要等待的信号 if ((err = pthread_sigmask(SIG_BLOCK, &mask, &oldmask)) != 0) perror("SIG_BLOCK error!\n") ; //创建线程 if ((err = pthread_create(&tidbuf[0], NULL, thr1, NULL)) != 0) perror("can't create thread!\n") ; if ((err = pthread_create(&tidbuf[1], NULL, thr2, NULL)) != 0) perror("can't create thread!\n") ; //等待线程结束 pthread_join(tidbuf[0], NULL) ; pthread_join(tidbuf[1], NULL) ; exit(0) ; } void* thr1(void* arg) { int err, signo ; //等待线程2起来 pthread_mutex_lock(&lock) ; while(flag == 0) pthread_cond_wait(&waitcond, &lock) ; pthread_mutex_unlock(&lock) ; //等待接收来自线程2的信号 if ((err = sigwait(&mask, &signo)) != 0) perror("sigwait error!\n") ; if (signo == SIGUSR1) printf("\nI catched the signal from thread2!\n") ; } // void* thr2(void* arg) { int err, signo ; //起来之后向线程1发送信号 pthread_mutex_lock(&lock) ; flag = 1 ; pthread_mutex_unlock(&lock) ; pthread_cond_signal(&waitcond) ; pthread_kill(tidbuf[0], SIGUSR1) ; }
【注意】很多linux系统在编译时,没有自动链接线程库,需要在编译时指定链接:例 gcc a.c -l pthread
相关文章推荐
- UNIX/Linux-进程控制(实例入门篇)
- UNIX/Linux-线程(实例入门篇)
- UNIX/Linux-进程控制(实例入门篇)
- Unix/Linux下删除Oracle控制文件Controlfile为什么实例Instance没有立即奔溃?
- Unix/Linux下删除Oracle控制文件Controlfile为什么实例Instance没有立即奔溃?
- Unix/Linux下删除Oracle控制文件Controlfile为什么实例Instance没有立即奔溃?
- 10个 Linux/Unix下 Bash 和 KSH shell 的作业控制实例
- UNIX/Linux-线程同步(实例入门篇)
- UNIX/Linux-进程环境(实例入门篇)
- Linux下的C编程实战(四)――“线程”控制与“线程”通信编程
- Unix环境高级编程:进程控制-线程控制-僵尸进程
- pthread_create是类Unix操作系统(Unix、Linux、Mac OS X等)的创建线程的函数。
- Unix/Linux IPC及线程间通信总结
- Linux/Unix下makefile文件编写实例
- linux线程控制&线程分离
- Linux学习--线程控制
- Linux 进程/线程控制
- Linux/UNIX进程控制(1)
- UNIX环境高级编程 第十二章:线程控制
- linux 本地通信实例 AF_UNIX