天津模板建站哪家好,平面设计网课哪个机构好,淘宝关键词指数查询,scratch编程网站问题聚焦#xff1a;在简单地介绍线程的基本知识之后#xff0c;主要讨论三个方面的内容#xff1a; 1 创建线程和结束线程#xff1b; 2 读取和设置线程属性#xff1b; 3 线程同步方式#xff1a;POSIX信号量#xff0c;互斥锁和条件变量。Linux线程概述线程… 问题聚焦 在简单地介绍线程的基本知识之后主要讨论三个方面的内容 1 创建线程和结束线程 2 读取和设置线程属性 3 线程同步方式POSIX信号量互斥锁和条件变量。Linux线程概述线程模型程序中完成一个独立任务的完整执行序列即一个可调度的实体。分为内核线程和用户线程当进程的一个内核线程获得CPU的使用权时它就加载并运行一个用户线程可见内核线程相当于用户线程运行的“容器”。一个进程可以拥有M个内核线程和N个用户线程 MN。线程实现完全在用户空间实现线程的特点创建和调度线程都无须内核的干预因此速度相当快。不占用额外的内核资源很多线程不会对系统性能造成明显影响。(缺点)一个进程的多个线程无法运行在不同的CPU上完全在内核空间实现线程的优缺点则和上面的实现相反优缺点也互换。双层调度模式是前两种实现模式的混合体内核调度M个内核线程线程库调度N个用户线程不会过度消耗内核资源又可以充分利用多处理器的优势创建线程和结束线程基础API创建pthread_create定义#include pthread.h
int pthread_create ( pthread_t* thread, const pthread_attr_t* attr,void * (*start_routine)(void*) , void* arg);参数说明thread新线程的标识符 实际是一个整型并且Linux上几乎所有的资源标识符都是一个整型数比如socket。attr用于设置新线程的属性传递NULL表示使用默认线程属性start_routing和arg分别指定新线程将运行的函数及其参数。返回成功时返回0失败时返回错误码。一个用户可以打开的线程数量不能超过RLIMIT_NPROC软资源限制。此外系统上所有能创建的线程总数也不能超过/proc/sys/kernel/threads-max 内核参数所定义的值。退出线程pthread_exit定义#include pthread.h
void pthread_exit ( void* retval );pthread_exit函数通过retval参数向线程的回收者传递其退出信息。它执行完之后不会返回到调用者而且永远不会失败。回收其他线程pthread_join定义#include pthread.h
int pthread_join( pthread_t thread, void** retval ); 一个进程中的所有线程都可以调用pthread_join函数来回收其他线程即等待其他线程结束这类似于回收进程的wait和waitpid系统调用。参数说明thread目标线程的标识符retval目标线程返回的退出信息效果该函数会一直阻塞直到被回收的线程结束为止返回成功时返回0失败时返回错误码。取消线程pthread_cancel功能 有时候我们希望异常终止一个线程即取消线程。定义#include pthread.h
int pthread_cancel ( pthread_t thread ); 接收到取消请求的目标线程可以决定是否允许被取消以及如何取消。这分别由如下两个函数完成#include pthread.h
int pthread_setcancelstate( int state, int *oldstate );
int pthread_setcanceltype ( int type, int *oldtype );这两个函数的第一个参数分别用于设置线程的取消状态是否允许取消和取消类型如何取消。第二个参数分别记录线程原来的取消状态和取消类型。state参数有两个可选值PTHREAD_CANCEL_ENABLE允许线程被取消。它是线程被创建时默认取消状态。PTHREAD_CANCEL_DISABLE禁止线程被取消。这种情况下如果一个线程收到取消请求则它会将请求挂起直到该线程允许被取消。type参数也有两个可选值PTHREAD_CANCEL_ASYNCHRONOUS线程随时都可以被取消。它将使得接收到取消请求的目标线程立即采取行动。PTHREAD_CANCEL_DEFERRED允许目标线程推迟行动直到它调用了下面几个所谓的取消点函数中的一个pthread_join、pthread_testcancel、pthread_cond_wait、pthread_cond_timewait、sem_wait和sigwait。pthread_setcancelstate和pthread_setcanceltype 成功时返回0失败则返回错误码。线程属性pthread_attr_t结构体完整的线程属性。定义#include bits/pthreadtypes.h
#define __SIZEOF_PTHREAD_ATTR_T 36
typedef union
{char __size[__SIZEOF_PTHREAD_ATTR_T];long int __align;
} pthread_attr_t;各种线程属性全部包含在一个字符数组中并且线程库定义了一系列函数操作pthread_attr_t类型的变量以方便我们获取和设置线程属性。这些函数包括#include pthread.h
/*初始化线程属性对象*/
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 );
int pthread_attr_getstackaddr( const pthread_attr_t* attr,void **stackaddr );
int pthread_attr_setstackaddr( pthread_attr_t* attr,void* stackaddr );
int pthread_attr_getstacksize( const pthread_attr_t* attr,size_t* stacksize );
int pthread_attr_setstacksize( pthread_attr_t* attr,size_t stackszie );
int pthread_attr_getstack( const pthread_attr_t* attr,void** stackaddr,size_t* stacksize );
int pthread_attr_setstack( pthread_attr_t* attr,void* stackaddr,size_t stacksize );
int pthread_attr_getguardsize( const pthread_attr_t* attr,size_t* guarsize );
int pthread_attr_setguardsize( pthread_attr_t* attr,size_t guarsize );
int pthread_attr_getschedparam( const pthread_attr_t* attr,struct sched_param* param );
int pthread_attr_setschedparam( pthread_attr_t* attr,const struct sched_param* param );
int pthread_attr_getschedpolicy( const pthread_attr_t* attr,int* policy );
int pthread_attr_setschedpolicy( pthread_attr_t* attr,int policy );
int pthread_attr_getinheritsched( const pthread_attr_t* attr,int* inherit );
int pthread_attr_setinheritsched( pthread_attr_t* attr,int inherit );
int pthread_attr_getscope( const pthread_attr_t* attr,int* scope );
int pthread_attr_setscope( pthread_attr_t* attr,int scope );下面我们详细讨论每个线程属性的含义detachstate线程的脱离状态。它有PTHREAD_CREATE_JOINABLE和PTHREAD_CREATE_DETACH两个可选值。前者指定线程是可以被回收的后者使调用线程脱离与进程中其他线程的同步。脱离了与其他线程同步的线程称为“脱离线程”。脱离线程在退出时将自行释放其占用的系统资源。线程创建时该属性的默认值是PTHREAD_CREATE_JOINABLE。stackaddr和stacksize线程堆栈的起始地址和大小。一般来说我们不需要自己来管理线程堆栈因为Linux默认为每个线程分配了足够的堆栈空间一般是8 MB。我们可以使用ulimt -s 命令查看或修改这个默认值。guardsize保护区域大小。如果guardsize大于0则系统创建线程的时候会在其堆栈的尾部额外分配guardsize字节的空间作为保护堆栈不被错误的覆盖的区域。如果guardsize等于0则系统不为新创建的线程设置堆栈保护区。如果使用者通过pthread_attr_setstackaddr或pthread_attr_setstack函数手动设置线程的堆栈则guardsize属性将被忽略。schedparam线程调度参数。其类型时sched_param结构体。该结构体面前还只有一个整型类型的成员——sched_priority该成员表示线程的运行优先级。schedpolicy线程调度策略。该属性有SCHED_FIFO、SCHED_RR和SCHED_OTHER三个可选值其中SCHED_OTHER是默认值。SCHED_RR表示采用轮转算法调度SCHED_FIFO表示使用先进先出的方法调度这两种调度方法都具备实时调度功能但只能用于以超级用户身份运行的进程。inheritsched是否继承调用线程的调度属性。该属性有PTHREAD_INHERIT_SCHED和PTHREAD_EXPLICIT_SCHED 两个可选值。前者表示新线程沿用其创建者的线程调度参数这种情况下再设置新线程的调度参数将没有任何效果。后者表示调用者要明确的指定新线程的调度参数。scope线程间竞争CPU的范围即线程优先级的有效范围。POSIX标准定义了该属性的PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS两个可选值前者表示目标线程与系统中所有线程一起竞争CPU后者表示目标线程仅与其他隶属于同一进程的线程竞争CPU的使用。面前Linux 只支持PTHREAD_SCOPE_SYSTEM这一种取值。接下来我们讨论3种专门用于线程同步的机制POSIX信号量互斥量和条件变量POSIX信号量常用API#include semaphore.h
int sem_init ( sem_t* sem, int pshared, unsigned int value ); // 初始化一个未命名的信号量
int sem_destroy ( sem_t* sem ); // 用于销毁信号量以释放其占用的内核资源
int sem_wait ( sem_t*sem ); // 以原子操作的方式将信号量减1如果信号量的值为0则阻塞直到该值不为0.
int sem_trywait ( sem_t* sem ); // sem_wait的非阻塞版本
int sem_post ( sem_t* sem ); // 以原子操作的方式将信号量的值加1sem_init函数用于初始化一个未命名的信号量。pshared参数指定信号量的类型。如果其值是0就表示这个信号量是当前进程的局部信号量否则该信号量就可以在多个进程之间共享。value参数指定信号量的初始值。此外初始化一个已经被初始化的信号量将导致不可预期的结果。sem_destroy函数用于销毁信号量以释放其占用的内核资源。如果销毁一个正被其他线程等待的信号量则将导致不可预期的结果。sem_wait函数以原子操作的方式将信号量的值减1.如果信号量的值为0则sem_wait将被阻塞直到这个信号量具有非0值。sem_trywait与sem_wait函数相似不过它始终立即返回而不论被操作的信号量是否具有非0值相当于sem_wait的非阻塞版本。当信号量的值大于0值时sem_trywait对信号量执行减1操作。当信号量的值为0时它将返回-1并设置errno为EAGAIN。sem_post函数以原子操作的方式将信号量的值加1.当信号量的值大于0时其他正在调用sem_wait等待信号量的线程将被唤醒。这些函数成功时返回0失败时返回-1并设置errno。互斥锁作用用于保护关键代码段以确保其独占式访问。基础API定义#include pthread.h
int pthread_mutex_init ( pthread_mutex_t* mutex, const pthread_mutexattr_t* mutexattr );
int pthread_mutex_destroy ( pthread_mutex_t* mutex );
int pthread_mutex_lock ( pthread_mutex_t* mutex );
int pthread_mutex_trylock ( pthread_mutex_t* mutex );
int pthread_mutex_unlock ( pthread_mutex_t* mutex ); pthread_mutex_init函数用于初始化互斥锁。mutexattr参数指定互斥锁的属性。如果将它设置为NULL则表示使用默认属性。除了pthread_mutex_init函数我们还可以如下初始化一个互斥锁pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER;宏PTHREAD_MUTEX_INITIALIZER实际上只是把互斥锁的各个字段都初始化为0. pthread_mutex_destroy函数用于销毁互斥锁以释放其占用的内核资源。销毁一个已经加锁的互斥锁将导致不可预期的后果。 如果互斥锁已经被加锁pthread_mutex_trylock将返回错误码EBUSY。互斥锁属性pthread_mutexattr_t结构体中定义了一套完整的互斥锁属性这里我们列出其中一些主要的函数#include pthread.h
/* 初始化互斥锁属性对象 */
int pthread_mutexattr_init ( pthread_mutexattr_t* attr );
/* 销毁互斥锁属性对象 */
int pthread_mutexattr_destroy ( pthread_mutexattr_t* attr );
/* 获取和设置互斥锁的pshared属性 */
int pthread_mutexattr_getpshared ( const pthread_mutexattr_t* attr, int * pshared );
int pthread_mutexattr_setpshared ( pthread_mutexattr_t* attr, int pthread );
/* 获取和设置互斥锁的type属性 */
int pthread_mutexattr_gettype ( const pthread_mutexattr_t* atr, int * type );
int pthread_mutexattr_settype ( pthread_mutexattr_t* attr, int type );这里提到了两种常用属性pshared 和typepshared指定是否允许跨进程共享互斥锁其可选值有两个PTHREAD_PROCESS_SHARED互斥锁可以被跨进程共享。PTHREAD_PROCESS_PRIVATE互斥锁只能被和锁的初始化线程隶属于同一个进程的线程共享。type指定互斥锁的类型Linux支持如下4种类型的互斥锁PTHREAD_MUTEX_DEFAULT默认锁(缺省的互斥锁类型属性)。如果一个线程试图对一个默认锁重复锁定或者试图解锁一个由别的线程锁定的默认锁或者试图解锁已经被解锁的默认锁会引发不可预料的结果。PTHREAD_MUTEX_NORMAL普通锁。当一个线程对一个普通锁加锁以后其余请求该锁的线程将形成一个等待队列并在该锁解锁后按优先级获得锁。这种锁类型保证了资源分配的公平性。但这种锁也很容易引发问题1一个线程如果对一个已经加锁的普通锁再次加锁将引发死锁。2对一个已经被其他线程加锁的普通锁解锁或者对一个已经解锁的普通锁再次解锁将导致不可预期的后果。PTHREAD_MUTEX_ERRORCHECK检错锁。 如果一个线程试图对一个互斥锁重复锁定将会返回一个错误码EDEADLK。 如果试图解锁一个由别的线程锁定的互斥锁或者试图解锁已经被解锁的互斥锁则解锁操作返回EPERM。PTHREAD_MUTEX_RECURSIVE嵌套锁。如果一个线程对这种类型的互斥锁重复上锁不会引起死锁不过一个线程对这类互斥锁的多次重复上锁必须由这个线程来重复相同数量的解锁这样才能解开这个互斥锁别的线程才能得到这个互斥锁。如果对一个已经被其他线程加锁的嵌套锁解锁或者对一个已经解锁的嵌套锁再次解锁则解锁操作返回EPERM。这种类型的互斥锁只能是进程私有的作用域属性为PTHREAD_PROCESS_PRIVATE。死锁结果导致一个或多个线程被挂起而无法继续执行原因对一个已经加锁的普通锁再次枷锁将导致死锁这种情况可能出现在不够仔细的递归函数中。如果两个线程按照不同顺序申请两个互斥锁也容易产生死锁。举例这段代码或许总能成功的运行不加入sleep函数刻意导致死锁但是会为程序留下一个潜在的bug。#include pthread.h
#include unistd.h
#include stdio.hint a 0;
int b 0;
pthread_mutex_t mutex_a;
pthread_mutex_t mutex_b;void* another( void* arg )
{pthread_mutex_lock( mutex_b );printf( in child thread, got mutex b, waiting for mutex a\n );sleep( 5 );b;pthread_mutex_lock( mutex_a );b a;pthread_mutex_unlock( mutex_a );pthread_mutex_unlock( mutex_b );pthread_exit( NULL );
}int main()
{pthread_t id;pthread_mutex_init( mutex_a, NULL );pthread_mutex_init( mutex_b, NULL );pthread_create( id, NULL, another, NULL );pthread_mutex_lock( mutex_a );printf( in parent thread, got mutex a, waiting for mutex b\n );sleep( 5 );a;pthread_mutex_lock( mutex_b );a b; pthread_mutex_unlock( mutex_b );pthread_mutex_unlock( mutex_a );pthread_join( id, NULL );pthread_mutex_destroy( mutex_a );pthread_mutex_destroy( mutex_b );return 0;
}条件变量互斥锁的作用用于同步线程对共享数据的访问。条件变量用于在线程之间同步同享数据的值。作用提供了一种线程间通知的机制当某个共享数据达到某个值的时候唤醒等待这个共享数据的线程。相关函数#include pthread.h
int pthread_cond_init (pthread_cond_t* cond, const pthread_condattr_t* cond_attr);
int pthread_cond_destroy ( pthread_cond_t* cond );
int pthread_cond_broadcast ( pthread_cond_t* cond ); //以广播的形式唤醒一个等待目标条件变量的线程
int pthread_cond_signal ( pthread_cond_t* cond ); //唤醒一个等待目标条件变量的线程
int pthread_cond_wait ( pthread_cond_t* cond, pthread_mutex_t* mutex ); // 等待目标条件变量mutex参数保证对条件变量及其等待队列的操作原子性。pthread_cond_init函数用于初始化条件变量。cond_attr参数指定条件变量的属性。如果将它设置为NULL则表示使用默认属性。除了pthread_cond_init函数外还可以如下初始化一个条件变量pthread_cond_t cond PTHREAD_COND_INITIALIZER;宏PTHREAD_COND_INITIALIZER实际上只是把条件变量的各个字段都初始化为0.pthread_cond_destroy 用于销毁条件变量以释放其占用的内核资源。销毁一个正在被等待的条件变量将失败并返回EBUSY。pthread_cond_broadcast 函数以广播的方式唤醒所有等待目标条件变量的线程。pthread_cond_signal 函数用于唤醒一个等待目标条件变量的线程。至于哪个线程将被唤醒则取决于线程的优先级和调度策略。有时候我们可能想唤醒一个指定线程但pthread没有对该需求提供解决方案。不过我们可以间接实现该需求定义一个能够唯一表示目标线程的全局变量在唤醒等待条件变量的线程前先设置该变量为目标线程然后采用广播方式唤醒所有等待条件变量的线程这些线程被唤醒后都检查该变量以判断被唤醒的是否是自己如果是就开始执行后续代码如果不是则返回继续等待。pthread_cond_wait 函数用于等待目标条件变量。mutex参数是用于保护条件变量的互斥锁以确保pthread_cond_wait 操作的原子性。在调用pthread_cond_wait 前必须确保互斥锁mutex已经加锁否则将导致不可预期的结果。pthread_cond_wait 函数执行时首先把调用线程放入条件变量的等待队列中然后将互斥锁mutex解锁。 可见从pthread_cond_wait 开始执行到其调用线程被放入条件变量的等待队列之间的这段时间内pthread_cond_signal 和pthread_cond_broadcast 等函数不会修改条件变量。换言之pthread_cond_wait 函数不会错过目标条件变量的任何变化。当pthread_cond_wait 函数成功返回时互斥锁mutex将再次被锁上。对三种同步机制的封装这是原书的代码我也没有加注释上面的api都了解了这里也不会有什么问题。#ifndef LOCKER_H
#define LOCKER_H#include exception
#include pthread.h
#include semaphore.hclass sem
{
public:sem(){if( sem_init( m_sem, 0, 0 ) ! 0 ){throw std::exception();}}~sem(){sem_destroy( m_sem );}bool wait(){return sem_wait( m_sem ) 0;}bool post(){return sem_post( m_sem ) 0;}private:sem_t m_sem;
};class locker
{
public:locker(){if( pthread_mutex_init( m_mutex, NULL ) ! 0 ){throw std::exception();}}~locker(){pthread_mutex_destroy( m_mutex );}bool lock(){return pthread_mutex_lock( m_mutex ) 0;}bool unlock(){return pthread_mutex_unlock( m_mutex ) 0;}private:pthread_mutex_t m_mutex;
};class cond
{
public:cond(){if( pthread_mutex_init( m_mutex, NULL ) ! 0 ){throw std::exception();}if ( pthread_cond_init( m_cond, NULL ) ! 0 ){pthread_mutex_destroy( m_mutex );throw std::exception();}}~cond(){pthread_mutex_destroy( m_mutex );pthread_cond_destroy( m_cond );}bool wait(){int ret 0;pthread_mutex_lock( m_mutex );ret pthread_cond_wait( m_cond, m_mutex );pthread_mutex_unlock( m_mutex );return ret 0;}bool signal(){return pthread_cond_signal( m_cond ) 0;}private:pthread_mutex_t m_mutex;pthread_cond_t m_cond;
};#endif
线程和进程思考这样一个问题如果一个多线程程序的某个线程调用了fork函数那么新创建的子进程是否将自动创建和父进程相同的数量的线程呢答案是“否”。子进程只拥有一个执行线程它是调用fork的那个线程的完整复制。并且子进程将自动继承父进程中互斥锁条件变量之类 的状态。也就是说父进程中已经被加锁的互斥锁在子进程中也是被锁住的。这就引起了一个问题子进程可能不清楚从父进程继承而来的互斥锁的具体状态是加锁状态还是解锁状态。这个互斥锁可能被加锁了但并不是由调用fork函数的那个线程锁住的而是由其他线程锁住的。如果是这种情况则子进程若再次对该互斥锁执行加锁操作就会导致死锁。#include pthread.h
#include unistd.h
#include stdio.h
#include stdlib.h
#include wait.hpthread_mutex_t mutex;/*子线程运行的函数。它首先获得互斥锁mutex然后暂停5s 再释放该互斥锁*/
void* another( void* arg )
{printf( in child thread, lock the mutex\n );pthread_mutex_lock( mutex );sleep( 5 );pthread_mutex_unlock( mutex );
}void prepare()
{pthread_mutex_lock( mutex );
}void infork()
{pthread_mutex_unlock( mutex );
}int main()
{pthread_mutex_init( mutex, NULL );pthread_t id;pthread_create( id, NULL, another, NULL );//pthread_atfork( prepare, infork, infork );/*父进程中的主线程暂停1s以确保在执行fork操作之前子线程已经开始运行并获得了互斥变量mutex*/sleep( 1 );int pid fork();if( pid 0 ){pthread_join( id, NULL );pthread_mutex_destroy( mutex );return 1;}else if( pid 0 ){printf( I anm in the child, want to get the lock\n );/*子进程从父进程继承了互斥锁mutex的状态该互斥锁处于锁住的状态这是由父进程中的子线程执行pthread_mutex_lock引起的因此下面这句加锁操作会一直阻塞尽管从逻辑上来说它是不应该阻塞的*/pthread_mutex_lock( mutex );printf( I can not run to here, oop...\n );pthread_mutex_unlock( mutex );exit( 0 );}else{pthread_mutex_unlock( mutex );wait( NULL );}pthread_join( id, NULL );pthread_mutex_destroy( mutex );return 0;
}
不过pthread提供了一个专门的函数pthread_atfork以确保fork调用后父进程和子进程都拥有一个清楚的锁状态。该函数的定义如下#include pthread.h
int pthread_atfork( void (*prepare)(void), void (*parent)(void),void (*child)(void) );该函数将建立3个fork句柄来帮助我们清理互斥锁的状态。该函数成功时返回0失败则返回错误码。prepare 句柄将在fork调用创建出子进程之前被执行。它可以用来锁住父进程中的互斥锁。parent 句柄则是fork调用创建出子进程之后而fork返回之前在父进程中被执行。它的作用是释放所有在prepare 句柄中被锁住的互斥锁。child 句柄是在fork 返回之前在子进程中被执行。和parent句柄一样child 句柄也是用于释放所有在prepare 句柄中被锁住的互斥锁。使用pthread_atfork函数void prepare()
{pthread_mutex_lock( mutex );
}void infork()
{pthread_mutex_unlock( mutex );
}pthread_atfork( prepare, infork, infork );线程和信号每个线程都可以独立的设置信号掩码。进程信号掩码的函数sigpromask但在多线程环境下我们应该使用如下所示的pthread 版本的sigpromask函数来设置线程信号掩码#include pthread.h
include signal.h
int pthread_sigmask( int how, const sigset_t* newmask, sigset_t* oldmask );由于进程中的所有线程共享该进程的信号所以线程库将根据线程掩码决定把信号发送给哪个具体的线程。因此如果我们在每个子线程中都单独设置信号掩码就很容易导致逻辑错误。此外所有线程共享信号处理函数。也就是说当我们在一个线程中设置了某个信号的信号处理函数后它将覆盖其他线程为同一个信号设置的信号处理函数。这两点都说明我们应该定义一个专门的线程来处理所有的信号。这可以通过如下两个步骤来实现在主线程创建出其他子线程之前就调用pthread_sigmask来设置好信号掩码所有新创建的子线程都将自动继承这个信号掩码。这样做之后实际上所有线程都不会响应被屏蔽的信号了。在某个线程中调用如下函数来等待信号并处理之#include signal.h
int sigwait( const sigset_t* set, int* sig );set 参数指定需要等待的信号的集合。我们可以简单的将其指定为在第一步中创建的信号掩码表示在该线程中等待所有被屏蔽的信号。参数sig 指向的整数用于存储该函数返回的信号值。sigwait成功时返回0失败则返回错误码。一旦sigwait正确返回我们就可以对接收到的信号做处理了。很显然如果我们使用了sigwait就不应该再为信号设置信号处理函数了。这是因为当程序接收到了信号时二者中只能有一个起作用。#include pthread.h
#include stdio.h
#include stdlib.h
#include unistd.h
#include signal.h
#include errno.h/* Simple error handling functions */#define handle_error_en(en, msg) \do { errno en; perror(msg); exit(EXIT_FAILURE); } while (0)static void *sig_thread(void *arg)
{printf( yyyyy, thread id is: %ld\n, pthread_self() );sigset_t *set (sigset_t *) arg;int s, sig;for (;;) {/*第二步骤调用sigwait等待信号*/s sigwait(set, sig);if (s ! 0)handle_error_en(s, sigwait);printf(Signal handling thread got signal %d\n, sig);}
}int main(int argc, char *argv[])
{pthread_t thread;sigset_t set;int s;/*第一步骤在主线程中设置信号掩码*/sigemptyset(set);sigaddset(set, SIGQUIT);sigaddset(set, SIGUSR1);s pthread_sigmask(SIG_BLOCK, set, NULL);if (s ! 0)handle_error_en(s, pthread_sigmask);s pthread_create(thread, NULL, sig_thread, (void *) set);if (s ! 0)handle_error_en(s, pthread_create);printf( sub thread with id: %ld\n, thread );pause(); /* Dummy pause so we can test program */
}
pthread 还提供了下面的方法使得我们可以明确的将一个信号发送给指定的线程#include signal.h
int pthread_kill( pthread_t thread, int sig );thread参数指定目标线程。sig参数指定待发送的信号。如果sig为0则pthread_kill 不发送信号但它仍然会执行错误检查。我们可以利用这种方式来检测目标线程是否存在。pthread_kill 成功返回0失败则返回错误码。小结这篇看似内容很多其实大都是走马观花了解一下整个框架和常用的API起到一个入门和索引的作用真正用到的时候知道从哪里入手。这一本书的大概框架就被我们浏览完了排除了我不太感兴趣的几个章节如果需要的话我们会回头再来研究的。关于服务器编程目前来说只能算是兴趣了解一下并没有太深入这段时间事情比较多打算看点别的方面的资料后面的进度上可能会慢一点了。参考资料《Linux高性能服务器编程》from: http://blog.csdn.net/zs634134578/article/details/20706741 转载于:https://www.cnblogs.com/hehehaha/p/6332335.html