注意,线程创建和启动之间存在时间窗口。因此创建线程时通过pvArg参数传递的某块内存空间值,在线程启动例程中读取该指针所指向的内存时,该内存值可能已被主线程或其他新线程修改。为安全起见,可为每个需要传值的线程分配堆内存,创建时传递该内存地址(线程私有),而在新线程内部释放该内存。

  本节示例中,主线程仅向SigMgrThread线程传递信号屏蔽字,且主线程结束时进程退出。因此,尽管SigMgrThread线程已分离,但仍可直接使用创建线程时pvArg传递的信号屏蔽字。否则应使用全局屏蔽字变量,或在本函数内再次设置屏蔽字自动变量
  编译链接后,执行结果如下(无论是否定义USE_SIGWAIT):

 

1 Main thread b7fcd6c0 is running!
2 Create a signal manager thread b7fccb90!
3 Create another signal manager thread b75cbb90!
4 Create a worker thread b6bcab90!
5 Send signals...
6 ++Thread b7fccb90 Received Signal 10(User defined signal 1)!
7 ++Thread b7fccb90 Received Signal 12(User defined signal 2)!
8 ++Thread b7fccb90 Received Signal 34(Real-time signal 0)!
9 ++Thread b7fccb90 Received Signal 34(Real-time signal 0)!
10 ++Thread b7fccb90 Received Signal 36(Real-time signal 2)!
11 ++Thread b7fccb90 Received Signal 36(Real-time signal 2)!
12 ++Thread b7fccb90 Received Signal 64(Real-time signal 30)!
13 ++Thread b7fccb90 Received Signal 64(Real-time signal 30)!
14 Thread b6bcab90 starts to work!
15 --Thread b7fcd6c0 Received Signal  3(Quit)!      //Ctrl+
16 997 seconds left to sleep!
17 Thread b6bcab90 is alive!
18 ++Thread b7fccb90 Received Signal  2(Interrupt)! //Ctrl+C
19 ++Thread b7fccb90 Received Signal  2(Interrupt)! //Ctrl+C
20 --Thread b7fcd6c0 Received Signal  3(Quit)!      //Ctrl+
21 Segmentation fault

  以下按行解释和分析上述执行结果:
  【6~13行】相同的非实时信号(编号小于SIGRTMIN)不会在信号队列中排队,只被递送一次;相同的实时信号(编号范围为SIGRTMIN~SIGRTMAX)则会在信号队列中排队,并按照顺序全部递送。若信号队列中有多个非实时和实时信号排队,则先递送编号较小的信号,如SIGUSR1(10)先于SIGUSR2(12),SIGRTMIN(34)先于SIGRTMAX(64)。但实际上,仅规定多个未决的实时信号中,优先递送编号小者。而实时信号和非实时信号之间,或多个非实时信号之间,递送顺序未定义。
  注意,SIGRTMIN/SIGRTMAX在不同的类Unix系统中可能取值不同。NPTL线程库的内部实现使用两个实时信号,而LinuxThreads线程库则使用三个实时信号。系统会根据线程库适当调整SIGRTMIN的取值,故应使用SIGRTMIN+N/SIGRTMAX-N(N为常量表达式)来指代实时信号。用户空间不可将SIGRTMIN/SIGRTMAX视为常量,若用于switch…case语句会导致编译错误。
  通过kill –l命令可查看系统支持的所有信号。
  【6~13行】sigwait()函数是线程安全(thread-safe)的。但当tMgrThrdId和tMgrThrdId2同时等待信号时,只有先创建的tMgrThrdId(SigMgrThread)线程等到信号。因此,不要使用多个线程等待同一信号。
  【14行】调用pthread_create()返回后,新创建的线程可能还未启动;反之,该函数返回前新创建线程可能已经启动。
  【15行】SIGQUIT信号被主线程捕获,因此不会中断SigMgrThread中的sigwaitinfo()调用。虽然SIGQUIT安装(signal语句)在SigMgrThread内,由于主线程共享该处理行为,SIGQUIT信号仍将被主线程捕获。
  【16行】sleep()函数使调用进程被挂起。当调用进程捕获某个信号时,sleep()提前返回,其返回值为未睡够时间(所要求的时间减去实际休眠时间)。注意,sigwait()等到的信号并不会导致sleep()提前返回。因此,示例中发送SIGQUIT信号可使sleep()提前返回,而SIGINT信号不行。
  在线程中尽量避免使用sleep()或usleep(),而应使用nanosleep()。前者可能基于SIGALARM信号实现(易受干扰),后者则非常安全。此外,usleep()在POSIX 2008中被废弃。
  【17行】WorkerThread线程启动后调用pthread_detach()进入分离状态,主线程将无法得知其何时终止。示例中WorkerThread线程一直运行,故可通过ThreadKill()检查其是否存在。但需注意,这种方法并不安全。
  【18行】已注册信号处理捕获SIGINT信号,同时又调用sigwait()等待该信号。终后者等到该信号,可见sigwait()优先级更高。
  【19行】sigwait()调用从未决队列中删除该信号,但并不改变信号屏蔽字。当sigwait()函数返回时,它所等待的信号仍旧被阻塞。因此,再次发送SIGINT信号时,仍被sigwait()函数等到。
  【21行】通过pthread_sigmask()阻塞SIGSEGV信号后,sigwait()并未等到该信号。系统输出"Segmentation fault"错误后,程序直接退出。因此,不要试图阻塞或等待SIGSEGV等硬件致命错误。若按照传统异步方式使用 signal()/sigaction()注册信号处理函数进行处理,则需要跳过引发异常的指令(longjmp)或直接退出进程(exit)。注意,SIGSEGV信号发送至引起该事件的线程中。例如,若在主线程内解除对该信号的阻塞并安装处理函数sighandler(),则当SigMgrThread线程内发生段错误时,执行结果将显示该线程捕获SIGSEGV信号。
  本示例剔除用于测试的干扰代码后,即为“主线程-信号处理线程-工作线程”的标准结构。
  3.3 示例3
  本示例结合信号的同步处理与条件变量,以通过信号安全地唤醒线程。为简化实现,未作错误处理。

 

1 int gWorkFlag = 0;  //设置退出标志为假
2 sigset_t gBlkSigs;  //信号屏蔽字(等待信号集)
3
4 pthread_mutex_t gLock = PTHREAD_MUTEX_INITIALIZER;
5 pthread_cond_t gCond = PTHREAD_COND_INITIALIZER;
6
7 void *SigThread(void *pvArg)
8 {
9     pthread_detach(pthread_self());
10
11     int dwSigNo;
12     sigwait(&gBlkSigs, &dwSigNo);
13     if(dwSigNo != SIGUSR1)
14     {
15         printf("Unexpected signal %d! ", dwSigNo);
16         exit(1);
17     }
18     pthread_mutex_lock(&gLock);
19     gWorkFlag = 1;   //设置退出标志为真
20     pthread_mutex_unlock(&gLock);
21     pthread_cond_signal(&gCond);
22
23     return 0;
24 }
25
26 void *WkrThread(void *pvArg)
27 {
28     pthread_detach(pthread_self());
29     printf("Worker thread starts! ");
30
31     pthread_mutex_lock(&gLock);
32     while(gWorkFlag == 0)
33         pthread_cond_wait(&gCond, &gLock);
34     pthread_mutex_unlock(&gLock);
35     //以下代码不含共享数据,故不需要锁定
36     printf("Worker thread starts working... ");
37     int dwVal = 1;
38     while(1)
39         dwVal += 5;
40 }
41
42 int main(void)
43 {
44     sigemptyset(&gBlkSigs);
45     sigaddset(&gBlkSigs, SIGUSR1);
46     pthread_sigmask(SIG_BLOCK, &gBlkSigs, NULL);
47
48     pthread_t tSigThrdId, tWkrThrdId;
49     pthread_create(&tSigThrdId, NULL, SigThread, NULL);
50     pthread_create(&tWkrThrdId, NULL, WkrThread, NULL);
51
52     while(1);
53     exit(0);
54 }