linux高级编程基础系列:线程和线程属性
fork子进程存在的问题:1】开销大,内存映像要从父进程拷贝到子进程,所有的描述字要在子进程中复制等。尽管采用了写时复制技术,但总体来说还是太过昂贵。2】进程和进程间通信很麻烦。
线程有助于解决这两个问题,线程有时被称为轻量级进程,创建线程比创建进程要快10-100倍。
线程的优点:1、占用较少的系统资源;2、共享数据容易,改善程序结构;3、提高应用程序响应速度;4、使用多处理器效率更高。
线程的缺点:1、内存共享会导致互相干扰;2、一个线程崩溃会导致整个进程崩溃。
进程/线程应用对比
应用功能
线程
进程
创建
pthread_creat()
fork()、vfork()
退出
pthread_exit()
exit()
等待
pthread_join()
wait()、waitpid()
取消/终止
pthread_cancel()
abort()
读取ID
pthread_self()
getpid()
调度策略
SCHED_OTHER
SCHED_OTHER
SCHED_FIFO
SCHED_FIFO
SCHED_RR
SCHED_RR
通信机制
信号量、信号、互斥锁、
无名管道、有名管道、信号、
条件变量、读写锁
消息队列、信号量、共享内存
进程和线程创建时的资源分配:
子进程创建时,把父进程的堆,栈,BSS区,全局变量和代码段复制一份。
一个进程里面的所有线程共享进程指令,大多数数据,打开的文件(描述字),
信号处理程序和信号处置
,当前工作目录,用户ID和组ID。
每个线程都有自己的线程ID,寄存器集合(包括程序计数器和栈指针),栈(用于存放局部变量和返回地址),Errno,信号掩码,优先级等。
线程创建完成后,将拥有自己的线程属性和执行栈,并从调用程序那里继承信号的掩码和优先级。
备注:一个有多线程的进程fork()子进程时,子进程中只可能有一个线程
用户级线程和内核级线程:
用户级线程:1】内核并不知道线程的存在;2】用户层线程库会实现线程的创建、删除和调度;3】用户层的线程创建比较快;4】当某个线程要使用内核时,其他线程都会被挂起
内核级线程:1】内核直接支持线程;2】线程的创建、删除和调度都由内核来做;3】一个线程等待I/O阻塞时,其余线程可以继续运行;4】创建等操作要进出内核,速度会慢些;
线程创建:pthread_creat()
1、头文件:
#include <pthread.h> 编译时需加上 -lpthread
2、函数原型:extern int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict addr,
void *(*start_routine)(void *),void *restrict arg);
3、返回:成功返回0,失败返回非0
4、参数:
(1)第一个参数用来存储线程ID,参数为指向线程ID的指针。线程的ID在某个进程中唯一,但不同进程创建出来的线程,可能拥有相同的ID号。如果创建成功,此参数返回线程ID,如果设置为NULL,则不会返回生成的线程的标识值。
pthread_t类型如下:typedef unsigned long int pthread_t ;
__restrict:C99定义的新的关键字,将视其修饰的变量不与其他变量关联,主要用来提高编译效率
(2)第二个参数用来设置线程的属性。(下文会详细介绍),不设置则为NULL
(3)第三个参数是线程运行函数的起始地址,即在线程中运行哪个函数
(4)第四个参数为运行函数的参数地址。如果需要给自定义的函数传入多个参数,则需要使用一个包含这些参数的结构体地址。不传参则设置为NULL
线程退出:pthread_exit()
1、头文件:#include <pthread.h>
2、函数原型:void pthread_exit(void *value_ptr);
int pthread_join(pthread_t thread,void **value_ptr);
3、说明:调用pthread_exit()后线程即退出,参数value_ptr会被后来调用pthread_join()函数的线程获得。
这里每个线程有自己的独立的清理函数栈。
当在线程相关的主函数里面直接return的时候,就不会调用这些注册的函数了。注意这里所说的要匹配使用这两个函数。在Ubuntu中这两个函数确实是实现为宏,只用一个老是报错。
线程退出前操作(异常取消时使用):pthread_cleanup_push()、pthread_cleanup_pop()
1、函数定义:
void pthread_cleanup_push(void (*routine)(void *),void *arg);
void pthread_cleanup_pop(int execte);
2、用于自动释放资源。采用先入后出的栈管理结构。
3、pthread_cleanup_push()
入清理栈
参数void (*routine)(void *arg)在调用pthread_cleanup_push()时被压入清理函数栈,多次调用pthread_cleanup_push()将在清理函数栈中形成一个函数链,在执行该函数链时按照压栈的相反顺序弹出。
4、pthread_cleanup_pop() 弹出清理函数
参数execte表示在弹出清理函数时,是否同时执行该函数,0表示不执行,1表示执行,这个函数并不影响异常中止时清理函数的执行。
5、例子:
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
void cleanup()
{
printf(" Cleanup \n ");
}
void *test_cancel(void)
{
pthread_cleanup_push(cleanup,NULL);
printf("Test_cancel\n");
while(1)
{
printf("test message\n");
sleep(1);
}
pthread_cleanup_pop(1);
}
int main()
{
pthread_t tid;
pthread_create(&tid,NULL,(void *)test_cancel,NULL);
sleep(2);
pthread_cancel(tid);
pthread_join(tid,NULL);
}
执行结果:
test_cancel
test message
test message
cleanup
线程等待和子线程资源回收:pthread_join()
1、函数原型:extern int pthread_join(pthread_t __ch,void **__thread_return);
2、函数功能:阻塞调用当前线程的进程,直到线程退出。线程结束后,回收线程的资源和返回值
3、参数说明:
第一个参数为被等待的线程ID,此线程必须同调用它的进程相联系。即创建该线程时不能指明此线程为独立的线程,默认情况下为关联线程。
第二个参数为一个用户定义的指针,指向一个保存等待线程的完整退出状态的静态区域,可以用来存储被等待线程的返回值,如果被设置为NULL,此退出状态的信息就会丢失。
获取线程ID:pthread_self()
1、头文件:#include <pthread.h>
2、函数原型:pthread_t pthread_self(void)
3、返回值为线程的ID号
取消线程:pthread_cancel()
1、函数原型:extern int pthread_cancel(pthread_t __cancelthread)
2、说明:1】pthread_cancel()允许线程以受控方式终止进程中执行的任何线程。仅当目标线程的可取消状态设定为PTHREAD_CANCEL_ENABLE时,才可以进行取消。在默认状态下,此函数会使得由tid标志的线程的行为如同调用了参数为
PTHREAD_CANCEL_ENABLE的Pthread_exit函数,但是线程可以选择忽略取消方式或是控制取消方式。其仅仅是请求取消统一进程的其他线程。2】执行取消操作时,将调用取消清理处理程序(pthread_cleanup_push函数),调用取消清理处理程序的顺序和安装这些处理程序的顺序相反。3】pthread_cancel()的调用者将不会等待取消的目标线程操作完成才返回,只是发送该信号,如果执行成功,将返回0,否则返回错误编号,但并不设置errno变量。
设置线程可取消状态:pthread_setcancelstate()
1、函数原型:extern int pthread_setcancelstate(int __state,int *__oldstate);
2、参数:state为调用线程的可取消性状态所要设置的值,oldstate为存储调用线程原来的可取消性状态的内存地址。
3、可设置的state的合法值:
PTHREAD_CANCEL_DISABLE:
针对目标线程的取消请求将处于未决状态,启用取消后才执行取消请求
。
PTHREAD_CANCEL_ENABLE:针对目标线程的取消请求将被递送,默认情况下,可取消状态为enable。
设置线程的取消类型:pthread_setcanceltype()
1、函数原型:extern int pthread_setcanceltype(int __type,int *__oldtype)
2、参数:type为调用线程的可取消性类型所要设置的值。oldtype为存储调用线程原来的可取消类型的地址。
3、type的合法值:
PTHREAD_CANCEL_ASYNCHRONOUS:
可随时执行新的或未决的取消请求
PTHREAD_CANCEL_DEFERRED:
在目标线程到达一个目标点之前,取消请求将一直处于未决状态。
4、备注:1】
创建线程时,默认可取消类型被设置为deferred。
2】如果禁用了线程的可取消性状态,则线程的可取消性类型的设置不会生效,所有取消请求都保留为未决状态。但是一旦启用了可取消性,新设置的类型才会生效。
5、返回:成功返回0.失败返回错误编号指明错误,但不设置errno变量
线程的属性——pthread_creat的第二个参数
线程栈可通过pthread的API函数进行修改,线程属性包括:
属性名称
说明
detachstate
线程的分离状态属性
guardsize
线程栈末尾的警戒缓冲区大小
stackaddr
线程栈的最低地址
stacksize
线程栈的大小(字节)
线程属性的初始化:pthread_attr_init()、pthread_attr_destroy()
1、头文件:#include <pthread.h>
2、函数原型:extern int pthread_attr_init(pthread_attr_t *attr);
extern int pthread_attr_destroy(pthread_attr_t *attr);
3、返回值,成功返回0,失败返回-1;
线程分离函数:pthread_detach()
1、头文件:#include <pthread.h>
2、函数原型:int pthread_detach(pthread_t thread);
3、说明:
1】当一个线程被创建时,
系统给它创建了一个线程控制块(由thread id来标识)。如果线程没有设置分离属性,那么需要其他线程通过pthread_join()来回收这个线程控制块。如果设置了分离属性,那么线程结束时自动释放创建时分配的资源。
2】pthread_detach()用来设置分离属性,但必须注意在新创建的线程结束之前。
一般来说这个函数是由新建线程进入启动函数后立马调用的。调用格式如下:
pthread_detach(pthread_self());
线程分离属性设置:pthread_attr_setdetachstate()、pthread_attr_getdetachstate()
1、头文件:#include <pthread.h>
2、函数原型:extern int pthread_attr_setdetachstate(pthread_attr_t *__attr,int __detachstate);
extern int pthread_attr_getdetachstate(__const pthread_attr_t *__attr,int *__detachstate);
3、 参数:detachstate 属性的新值将传递给detachstate参数中的此函数,其合法值有:
PTHREAD_CREAT_DETACHED:此选项使得使用attr 创建的所有线程
(记住attr是pthread_creat函数的第二个参数)处于分离状态。线程终止时,系统将自动回收与带有此状态的线程的相关联的资源。这类线程不能被其他线程等待。
PTHREAD_CREAT_JOINABLE:此选项使得使用attr创建的所有的线程处于可连接状态,线程终止时,不会回收与带有此状态的线程相关联的资源。如果要回收这些资源,需调用pthread_detach()和pthread_join()函数。detachstate的缺省值为PTHREAD_CREAT_JOINABLE。
4、无论创建分离线程还是非分离线程,在所有的线程退出之前,进程都不能退出。
线程缓冲区大小设置:pthread_attr_setguardsize()、pthread_attr_getguardsize()
1、头文件:#include <pthread.h>
2、函数原型:extern int pthread_attr_getguardsize(__const pthread_attr_t *__attr,
size_t *__guardsize);
extern int pthread_attr_setguardsize(pthread_attr_t *__attr ,size_t __guardsize);
3、备注:
1】pthread_attr_getguardsize用来设置栈溢出后,还可以使用的栈的大小,一般为一个PAGESIZE,也称为警戒缓冲区。
2】如果我们对stackaddr做了修改,那么系统会假设我们自己会管理栈,不会提供警戒缓冲区给我们使用,相当于设置guardsize为0。
线程栈设置:pthread_attr_setstack()、pthread_attr_getstack()
1、头文件:#include <pthread.h>
2、函数原型:extern int pthread_attr_getstack(const pthread_attr_t *restrict addr,
void **restrict stackaddr ,size_t *restrict stacksize);
extern int pthread_attr_setstack(pthread_attr_t *addr,void *stackaddr,
size_t stacksize);
3、备注:pthread_attr_setstack函数的第二个参数是内存的起始地址,第三个参数为内存的大小。
线程栈空间大小设置:pthread_attr_setstacksize()、pthread_attr_getstacksize()
1、头文件:#include <pthread.h>
2、函数原型:extern int pthread_attr_setstacksize(pthread_attr_t *__attr,size_t __stacksize);
extern int pthread_attr_getstacksize(__const pthread_attr_t *__restrict __attr,
size_t *__restrict __stacksize);
3、备注:pthread_attr_setstacksize()可以在不处理栈分配的情况下,改变线程栈的大小。
线程栈基址设置:pthread_attr_setstackaddr()、pthread_attr_getstackaddr()
1、头文件:#include <pthread.h>
2、函数原型:extern int pthread_setstackaddr(pthread_attr_t *__attr,void *__stackaddr);
extern int pthread_getstackaddr(__const pthread_attr_t *__restric __attr,
void **__restrict __stackaddr);
linux高级编程基础系列:线程间通信
原文地址:http://blog.163.com/jimking_2010/blog/static/1716015352013102510748824/
线程间通信机制:
线程是一种轻量级的进程。
进程的通信机制主要包括
无名管道、有名管道、消息队列、信号量、共享内存以及信号等。这些机制都是由linux内核来维护的,实现起来都比较复杂,而且占用大量的系统资源。
线程间的通信机制实现起来则相对简单,主要包括
互斥锁、条件变量、读写锁和线程信号等。
本文会对以上所涉及的线程间的通信机制一一展开。
互斥锁通信机制:
1、
互斥锁基本原理:互斥锁以排他的方式防止数据被并发修改。当多个线程共享相同的内存时,需要确保每个线程看到的数据是一样的。如果是只读,那么一定是一样的。如果是可读可写,在一个线程操作一个内存区域时,包含三个步骤,即读出数据,修改数据,写回数据。如果该线程在没有写回数据前,另一个线程来访问同一个区域,如果是读,得不到最新的数据状态,如果是写,则会造成数据覆盖等问题。
互斥锁就两个状态:开锁(0),上锁(1)。将某个共享资源和互斥锁绑定后,对该共享资源的访问操作如下:
A】在访问资源前,首先申请该互斥锁,如果在开锁状态,则申请到该锁对象,并立即占有该锁(锁定)。以防其他线程访问修改此资源。如果该锁处于锁定状态,
默认阻塞等待
。
B】原则上只有锁定该互斥锁的进程才能释放此互斥锁。但实际上,非锁定的线程去解锁也能成功。这个与锁的条件有关,本文后续内容会详细介绍。
互斥锁基本操作函数如下:
功能
函数
初始化互斥锁
pthread_mutex_init()
阻塞申请互斥锁
pthread_mutex_lock()
释放互斥锁
pthread_mutex_unlock()
尝试加锁(非阻塞方式)
pthread_mutex_trylock()
销毁互斥锁
pthread_mutex_destroy()
2、
互斥锁的初始化和销毁:pthread_mutex_init()、pthread_mutex_destroy()
A】头文件:#include <pthread.h>
B】函数原型:extern int pthread_mutex_init(pthread_mutex_t *__mutex,__const pthread_mutexattr_t *__mutexattr);
extern int pthread_mutex_destroy(pthread_mutex_t *__mutex);
C】返回:操作成功返回0,不成功则返回非零值
D】参数:
a、第一个参数为指向要初始化/销毁的互斥锁的指针。pthread_mutex_t即互斥量类型。在使用互斥锁时,需在函数内定义一个这种类型的变量。其值可通过pthread_mutex_init()函数来以初始化,也可以通过使用pthread.h中定义的宏PTHREAD_MUTEX_INITIALIZER (只对静态分配的互斥量)来初始化。如果是动态分配的互斥量,那么释放内存前需要用pthread_mutex_destroy,初始化用pthread_mutex_init()。
pthread.h中宏定义如下:
#define PTHREAD_MUTEX_INITIALIZER { {0,} }
初始化方式如下:
pthread_mutex_t p = PTHREAD_MUTEX_INITIALIZER;
b、第二个参数mutexattr是指向属性对象的指针,该属性对象定义要初始化锁的属性。如果该指针为NULL,则表示使用默认属性。锁的属性在本文后续部分有详细的介绍。
3、
互斥锁的申请、释放和尝试解锁:pthread_mutex_lock()、pthread_mutex_unlock()、pthread_mutex_trylock()
A】函数原型:extern int pthread_mutex_lock(pthread_mutex_t *__mutex);
extern int pthread_mutex_trylock(pthread_mutex_t *__mutex);
extern int pthread_mutex_unlock(pthread_mutex_t *__mutex);
B】返回:成功返回0,失败返回一个错误编号,以指明错误。(pthread_mutex_unlock()未设置errno变量)
条件变量通信机制:
1、
条件变量基本原理:条件变量的出现,可以弥补互斥锁的缺陷,有些问题仅仅靠互斥锁无法解决。但是条件变量不能单独使用,必须配合互斥锁一起实现对资源的互斥访问。
例:互斥锁无法解决的问题。
int i = 3;int j = 7;
pthread A
pthread_B
pthread_mutex_lock();
pthread_mutex_lock()
{
{
i++;
if(i==j)
j--;
do_something();
}
}
pthread_mutex_unlock();
pthread_mutex_unlock();
——————————————————————————————————————
上例中:两个线程抢占互斥锁,可能会导致pthread B中的do_something()函数永远无法执行的情况。这是程序员不想看到的。仔细分析后,可得到线程B其实不需要一直的申请释放锁,其运行仅仅需要一种情况而已。在A线程满足i == j时,通知B线程执行do_something()即可。
条件变量基本操作:
功能
函数
初始化条件变量
pthread_cond_init()
阻塞等待条件变量
pthread_cond_wait()
通知等待该条件变量的第一个线程
pthread_cond_signal()
在指定的时间之内等待条件变量
pthread_cond_timedwait()
销毁条件变量状态
pthread_cond_destroy()
2、条件变量的初始化和销毁:pthread_cond_init()、pthread_cond_destroy()
A】函数原型:extern int pthread_cond_init(pthread_cond_t *__restrict __cond,__const pthread_condattr_t *__restrict __cond_attr);
extern int pthread_cond_destroy(pthread_cond_t *__cond);
B】返回:成功返回0,失败返回错误编号以指明错误。
C】参数:第一个参数指向要初始化或损坏的条件变量的指针,条件变量的类型为pthread_cond_t。第二个参数指向条件属性对象的指针。该属性对象定义要初始化的条件变量的特性,如果此变量初始化为NULL,则为默认属性。关于条件属性,本文后续会有详细介绍。
3、通知等待条件变量的线程:pthread_cond_signal()、pthread_cond_broadcast()
A】函数原型:extern int pthread_cond_signal(pthread_cond_t *__cond);
extern int pthread_cond_broadcast(pthread_cond_t *__cond);
B】说明:
a、
pthread_cond_signal()函数用于唤醒等待出现与条件变量cond相关联的条件的第一个线程。如果cond上没有阻塞任何线程,则此函数不起作用。
如果cond阻塞了多个线程,则调度策略将确定要取消阻塞的线程。显然,在此函数中,隐含的释放了当前线程占用的信号量(备注:信号和信号量不是一个东西,在进程和进程通信中会详细说明信号和信号量)。
b、pthread_cond_broadcast()函数用于唤醒等待出现与条件变量cond关联的条件的所有线程。如果cond上没有阻塞任何线程,则此函数不起作用。
4、等待条件变量:pthread_cond_wait()、pthread_cond_timedwait()
A】函数原型:extern int pthread_cond_wait(pthread_cond_t *__restrict __cond,pthread_mutex_t *__restrict __mutex);
extern int pthread_cond_timedwait( pthread_cond_t *__restrict __cond,pthread_mutex_t *__restrict __mutex ,__const struct timespec *__restrict __abstime);
B】参数说明:cond是指向要等待的条件变量的指针,mutex指向与条件变量cond关联的互斥锁的指针。pthread_cond_wait()、pthread_cond_timedwait()函数的实现是一个先对互斥锁进行解锁,再加锁的一个过程。pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)函数传入的参数mutex用于保护条件,因为我们在调用pthread_cond_wait时,
如果条件不成立我们就进入阻塞,但是进入阻塞这个期间,如果条件变量改变了的话,那我们就漏掉了这个条件。因为这个线程还没有放到等待队列上,所以调用pthread_cond_wait前要先锁互斥量,即调用pthread_mutex_lock(),pthread_cond_wait在把线程放进阻塞队列后,自动对mutex进行解锁,
使得其它线程可以获得加锁的权利。这样其它线程才能对临界资源进行访问并在适当的时候唤醒这个阻塞的进程。当pthread_cond_wait返回的时候又自动给mutex加锁。
Thread A:当满足条件的时候发送一个信号。
Thread B:先给一个mutex加锁,以便互斥访问count的值。在一个while循环里等待count值达到MAX_COUNT。因为当
某个条件满足时,可能会有多个线程被唤醒。所以需要判断条件是否还满足。
pthread_cond_wait首先把调用线程放入条件变量的等待队列,然后再释放mutex。当函数返回时,mutex又会被加上锁。最后对mutex解锁,让其他线程使用count变量。(加了写锁的等待就是占着茅坑不拉屎,有数据更新此操作域又执行不了写操作,只能先解锁咯~~~~)
pthread_cond_timedwait()多了一个参数,即abstime,abstime是从1970年1月1日00:00:00以来的秒数,是一个绝对时间。等待时间到,则不阻塞,往下执行程序。timespec结构体声明如下:
struct timespec
{
long tv_sec;
long tv_nsec;
};
C】返回:如果成功,返回0,失败则返回一个错误编号。
读写锁通信机制:
1、
读写锁基本原理:在对数据的读写操作时,往往是读占主要部分。为了满足当前能够允许多个读出,但只允许一个写入的需求。线程提供了读写锁来实现。读写锁基本原则如下:
A】如果有其他线程读数据,则允许其他线程执行读操作,但是不允许写操作。
B】如果有其他线程申请了写锁,则其他线程既不能申请读锁,也不能申请写锁
读写锁基本操作函数如下:
功能
函数
初始化读写锁
pthread_rwlock_init()
阻塞申请读锁
pthread_rwlock_rdlock()
非阻塞申请读锁
pthread_rwlock_tryrdlock()
阻塞申请写锁
pthread_rwlock_wrlock()
非阻塞申请写锁
pthread_rwlock_trywrlock()
释放锁(读锁和写锁)
pthread_rwlock_unlock()
毁坏读写锁
pthread_rwlock_destroy()
2、初始化/毁坏读写锁:pthread_rwlock_init()、pthread_rwlock_destroy()
A】函数原型:extern int pthread_rwlock_init(pthread_rwlock_t *__restrict __rwlock,__const pthread_rwlockattr_t *__restrict __attr);
extern int pthread_rwlock_destroy(pthread_rwlock_t *__rwlock);
B】返回:如果成功则返回0,否则返回一个错误编号,以指明错误。
C】参数说明:第一个参数指向要初始化的读写锁的指针。类型为pthread_rwlock_t。第二个参数为读写锁的属性。在本文后续部分会详细说明。
3、申请读锁,写锁和解除读写锁:pthread_rwlock_rdlock()、pthread_rwlock_tryrdlock()、pthread_rwlock_wrlock()、pthread_rwlock_trywrlock()、pthread_rwlock_unlock()
A】函数原型:extern int pthread_rwlock_rdlock(pthread_rwlock_t *__rwlock);
extern int pthread_rwlock_tryrdlock(pthread_rwlock_t *__rwlock);
extern int pthread_rwlock_wrlock(pthread_rwlock_t *__rwlock);
extern int pthread_rwlock_trywrlock(pthread_rwlock_t *__rwlock);
extern int pthread_rwlock_unlock(pthread_rwlock_t *__rwlock);
B】说明:成功返回0,失败则返回一个错误编号以表明错误。
调用pthread_rwlock_unlock时需注意:
a、如果调用此函数来释放读写锁定rwlock上的读锁定,但当前在此读写锁定上还保持着其他的读锁定,则读锁定将保持线程读锁定状态。只不过当前线程已经不是其所有者之一。
b、如果此函数释放读写锁的最后一个读锁,则对象将处于没有所有者的解锁状态。
c、如果调用此函数释放读写锁的写锁,则读写锁定将处于没有所有者的解锁状态。
线程信号:
线程是一种轻量级的进程,因此进程的信号同样适用于线程。
不过相对于进程信号,线程拥有与信号相关的私有数据——线程信号掩码,
则就决定了线程在信号操作时具有以下特性:
A】每个线程可以向别的线程发送信号,pthread_kill()函数用来完成这一操作。
B】每个线程都可以设置自己的阻塞集合。pthread_sigmask()用来完成这一操作。类似于进程中的sigprocmask()函数。主进程创建出来的线程继承主进程的掩码。
C】每个线程需要设置针对某信号的处理方式,但同一个进程中对某信号的处理方式只能有一个有效,即最后一次设置的处理方式。即在所有的线程里,
同一信号在任何线程里的对该信号的处理一定相同
D】
如果别的进程向当前进程发来一个信号,具体由哪个形成去处理,是未知的。
1、
向指定的线程发送信号:pthread_kill()
A】函数原型:extern int pthread_kill(pthread_t __threadid,int __signo)
B】参数说明:threadid是目标线程,sigo是要发送的信号。pthread_kill()函数用于请求将信号传送给线程,调用进程中,信号将被异步定向到线程。
如果signo为0,就会检查线程是否存在而不发送信号。
成功完成后,pthread_kill()将返回0。否则会返回一个错误编号,用于指明错误(未设置errno变量)。
2、
调用线程的信号掩码:pthread_sigmask()
A】函数原型:extern int pthread_sigmask(int __how,__const __sigset_t *restrict __newmask,__sigset_t *restrict __oldmask);
B】说明:函数类似于进程信号中的sigprocmask()函数。pthread_sigmask()用来检查或更改线程的信号掩码。(此部分可参考进程和进程通信 信号部分内容)。
C】参数:第一个参数how定义如何更改调用线程的信号掩码。其合法值有以下三个(此小点内容与sigprocmask()重复)
#define SIG_BLOCK
0
#define SIG_UNBLOCK 1
#define SIG_SETMASK
2
宏说明:
SIG_BLOCK:将第2个参数所描述的集合添加到当前进程阻塞的信号集中。
SIG_UNBLOCK:将第2个参数所描述的集合从当前进程阻塞信号集中删除。
SIG_SETMASK:不管之前的阻塞信号,仅设置当前的进程阻塞的集合为第2个参数描述的对象。
如果newmask值是个空指针,则参数how没有意义,且不会更改线程的阻塞信号集,因此该调用可以用于查询当前受阻塞的信号。
D】返回:执行成功后,返回0,失败后返回错误编号来指明错误(未设置errno变量),另外,如果由于某种原因pthread_sigmask()失败,那么线程的信号掩码将不会变化。
【附言】
上面提到的互斥锁的属性,条件变量的属性和读写锁的属性三块内容,在下一篇文章中整理。放置于线程和线程通信目录下,请关注。