`

线程和信号

阅读更多
    每个线程都有自己的信号屏蔽字,但是信号的处理是进程中的所有线程共享的。这意味着单个线程可以阻止某些信号,但当某个线程修改了与某个给定信号相关的处理行为以后,所有的线程都必须共享这个处理行为的改变。这样,如果一个线程选择忽略某个给定信号,那么另一个线程就可以通过以下两种方式撤销上述线程的信号选择:恢复信号的默认处理行为,或者为信号设置一个新的信号处理程序。
    进程中的信号是递送到单个线程的。如果一个信号与硬件故障相关,那么该信号一般会被发送到引起该事件的线程中去,而其他的信号则被发送到任意一个线程。
    在信号基础函数一节中讨论了进程如何使用 sigprocmask 函数来阻止信号发送,但该函数的行为在多线程的进程中并没有定义,线程中必须使用 pthread_sigmask。线程还可以通过 sigwait 等待一个或多个信号的出现,以及调用 pthread_kill 来向线程发送信号。
#include <signal.h>
int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
int sigwait(const sigset_t *restrict set, int *restrict signop);
int pthread_kill(pthread_t thread, int signo);
                         /* 所有函数返回值:若成功,返回 0;否则,返回错误编号 */

    pthread_sigmask 的参数同 sigprocmask,在此就不再赘述,只是该函数是工作在线程中,而且失败时返回错误码。
    sigwait 的 set 参数指定了线程等待的信号集。返回时,signop 指向的整数将包含发送信号的编号。如果信号集中的某个信号在 sigwait 调用时处于挂起状态,那么 sigwait 将无阻塞地返回。在返回之前,sigwait 将从进程中移除那些处于挂起等待状态的信号。如果具体实现支持排队信号,并且信号的多个实例被挂起,那么 sigwait 将会移除该信号的一个实例,其他的实例还要继续排队。
    为避免错误行为发生,线程在调用 sigwait 之前,必须阻塞那些它正在等待的信号。sigwait 会原子地取消信号集的阻塞状态,直到有新的信号被递送。在返回前,sigwait 将恢复线程的信号屏蔽字。如果信号在 sigwait 被调用时没有被阻塞,那么在线程完成对 sigwait 的调用之前会出现一个时间窗,在该时间窗中,信号就可以被发送给线程。
    使用 sigwait 的好处在于它可以简化信号处理,允许把异步产生的信号用同步的方式处理。为防止信号中断线程,可以把信号加到每个线程的信号屏蔽字中,然后可以安排专用线程处理信号。这些专用线程可以调用函数,不需要担心在信号处理程序中调用哪些函数是安全的,因为这些函数调用来自正常的线程上下文,而非会中断线程正常执行的传统信号处理程序。
    如果多个线程在 sigwait 的调用中因等待同一个信号而阻塞,那么在信号递送的时候,就只有一个线程可以从 sigwait 中返回。如果一个信号被捕获,而一个线程正在 sigwait 调用中等待同一信号,那么这时将由操作系统实现来决定以何种方式递送信号:操作系统可以让 sigwait 返回,也可以激活信号处理程序,但这两种情况不会同时发生。
    kill 函数可以把信号发送给进程,而要把信号发送给线程,可以调用 pthread_kill。可以传一个 0 值的 signo 来检查线程是否存在。注意,如果信号的默认动作是终止该进程,那么把信号传递给某个线程仍然会杀死整个进程。另外,由于闹钟定时器是进程资源,并且所有的线程共享相同的闹钟。所以进程中的多个线程不可能互不干扰地使用闹钟定时器。
    在前面sigsuspend 函数和 abort 函数中介绍 sigsuspend 函数时,曾给出一个在信号处理程序中设置标志来表明主程序应该退出的例子,那个例子中唯一可运行的控制线程就是主线程和信号处理程序,所以阻塞信号足以避免错失标志修改。在多线程中,需要使用互斥量来保护标志,如下例程序所示。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>

static int quitflag;		// set nonzero by thread
sigset_t	mask;

pthread_mutex_t	lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t	waitloc = PTHREAD_COND_INITIALIZER;

void *thr_fn(void *arg){
	int signo, err;
	for(;;){
		if((err=sigwait(&mask, &signo)) != 0){
			printf("sigwait error\n");
			exit(1);
		}
		switch(signo){
			case SIGINT:
				printf("interrupt\n");
				break;
			case SIGQUIT:
				pthread_mutex_lock(&lock);
				quitflag = 1;
				pthread_mutex_unlock(&lock);
				pthread_cond_signal(&waitloc);
				return (void *)0;
			default:
				printf("unexpected signal: %d\n", signo);
				exit(1);
		}
	}
}

int main(void){
	sigset_t oldmask;
	sigemptyset(&mask);
	sigaddset(&mask, SIGINT);
	sigaddset(&mask, SIGQUIT);
	pthread_sigmask(SIG_BLOCK, &mask, &oldmask);

	pthread_t	tid;
	if(pthread_create(&tid, NULL, thr_fn, NULL) != 0){
		printf("failed to create thread\n");
		exit(1);
	}

	pthread_mutex_lock(&lock);
	while(quitflag == 0)
		pthread_cond_wait(&waitloc, &lock);
	pthread_mutex_unlock(&lock);

	quitflag = 0;	// SIGQUIT has been caught and is blocked

	sigprocmask(SIG_SETMASK, &oldmask, NULL);
	exit(0);
}

    程序运行结果同前面的那一个。这个程序中不用依赖信号处理程序中断主线程,有专门的独立线程进行信号处理。在互斥量的保护下改动 quitflag 的值,这样主线程就不会在 pthread_cond_signal 时错失唤醒调用。
    这里需要注意的是,在主线程开始时阻塞 SIGINT 和 SIGQUIT。当创建线程进行信号处理程序时,新建线程继承了现有的信号屏蔽字。因为 sigwait 会解除信号的阻塞状态,所以只有一个线程可以用于信号的接收。这使得对主线程进行编码时不必担心来自这些信号的中断。
分享到:
评论

相关推荐

    一个用阻塞方式的socket 做的聊天程序,使用了多线程和信号量技术

    本示例是一个基于阻塞式socket、多线程和信号量技术的聊天程序。让我们深入探讨这些关键概念及其在聊天程序中的应用。 首先,**阻塞式socket** 是网络编程中最基本的通信机制。在阻塞模式下,当调用socket函数执行...

    Qt中线程跟信号槽的关系

    信号和槽的连接可以是直接的,也可以是间接的,甚至可以在不同线程之间进行。 在多线程环境中,信号槽机制保证了跨线程通信的安全性。Qt内部采用了一种称为"线程绑定"(Thread Affinity)的概念,每个QObject都有一...

    unix/linux下多线程/信号管理框架

    在Unix/Linux操作系统环境中,多线程和信号管理是并发编程中的关键组成部分。多线程允许一个进程内同时执行多个不同的任务,而信号则提供了一种进程间通信(IPC)的方式,用于通知进程某些事件的发生或者控制进程的...

    c++多线程同步——信号量

    二进制信号量只有0和1两个状态,通常用于互斥访问,而计数信号量可以有任意非负整数值,可以允许多个线程同时访问资源,但不超过其值。 在C++11及更高版本中,信号量被包含在`&lt;semaphore&gt;`头文件中,但是标准库并未...

    这是一个用阻塞方式的socket 做的聊天程序,使用了多线程和信号

    总结来说,这个聊天程序利用了阻塞式的socket进行网络通信,通过多线程来实现并发处理,使用信号量保证并发安全,同时具备用户友好的界面和丰富的功能。它是一个学习网络编程、多线程和同步机制的好案例。源码的提供...

    Linux线程编程之信号处理.rar

    在Linux系统编程中,线程和信号处理是两个至关重要的概念。线程允许在一个进程中并发执行多个代码流,而信号则是进程间通信的一种机制,用于通知进程某些事件的发生。本资料"Linux线程编程之信号处理"深入探讨了这两...

    多线程可以使用的,使用c语言,使用信号量

    本篇文章将深入探讨使用C语言实现多线程以及信号量的概念和应用。 首先,我们了解C语言中的多线程。由于C语言标准库不包含多线程支持,程序员需要依赖特定平台的接口,如POSIX线程(pthread)库在Unix或Linux系统中...

    易语言多线程控制:信号量控制线程数量

    信号量 = 创建信号量(5, 5) // 初始化一个信号量,初始值和最大值均为5,表示最多有5个线程并发执行 .线程池 = 创建数组(10, .线程) // 创建一个包含10个元素的线程数组 .任务计数 = 0 // 用于记录当前正在执行的...

    实现生成者消费者问题

    - **设计思想**:解释设计背后的理念,如使用线程和信号量的原因,以及如何实现线程间的同步和通信。 - **数据结构与模块说明**:定义缓冲区的数据结构,说明每个模块的功能,比如生产者模块、消费者模块和共享...

    qt5多线程,信号量,互斥量,等待条件

    本文将详细探讨如何利用QT5的线程机制,以及信号量(Semaphore)、互斥量(Mutex)和等待条件(Condition Variable)等同步原语,来构建一个高效的生产者-消费者模型。 首先,我们要理解线程的基本概念。线程是程序...

    windowsC++多线程加锁信号量共享内存

    在"threadlock4"这个文件中,很可能包含了示例代码或者详细教程,演示了如何在Windows环境下使用C++的多线程、互斥锁、信号量和共享内存。通过阅读和理解这个文件,开发者可以学习到如何在实际项目中有效地管理和...

    有关线程知识

    根据提供的文件信息,我们可以从中提炼出关于线程管理和信号量操作的相关知识点。下面将详细解释这些知识点,并尽可能地提供丰富的信息。 ### 线程基础知识 #### 什么是线程? 线程是操作系统能够进行运算调度的...

    Linux上多线程C++应用程序的信号处理程序_C++_CMake_下载.zip

    在Linux系统中,多线程C++应用程序常常需要处理各种信号(signals)来实现异步事件的响应。信号是操作系统提供的一种进程间...在实际项目中,可以结合CMakeLists.txt文件,了解如何配置项目以支持多线程和信号处理。

    Reader-Writer-Threading:java线程和信号量的读写器问题

    描述中提到的"java线程和信号量的读写器问题",进一步强调了使用Java线程和信号量(Semaphore)来实现读写器问题的解决方案。Semaphore是一种有限资源的计数信号量,可以用来控制同时访问特定资源的线程数量。 在...

    线程间同步机制 读写锁通信机制 线程与信号

    在Linux高级程序设计中,主要介绍了三种线程同步机制:互斥锁、条件变量和读写锁,以及线程与信号的交互。 1. **互斥锁通信机制**: 互斥锁是用于保护临界区的一种机制,确保同一时间只有一个线程能访问共享资源。...

    操作系统实验:多线程并发程序设计模拟生产者消费问题

    实验旨在让学生了解如何通过多线程并发编程来模拟生产者消费者问题,以及如何使用P-V操作(即信号量机制)来实现生产者和消费者的同步和互斥。P操作(Wait或Acquire)用于请求访问临界资源,而V操作(Signal或...

    VC线程信号量的使用举例

    本文将通过一个简单的VC线程信号量使用例子来介绍其工作原理和应用。 信号量是一种同步机制,用于控制对共享资源的访问。它可以计数,当计数值大于0时,表示资源可用;计数值为0,则表示资源已被全部占用。线程试图...

    XinHaoLiang.rar_线程

    线程是操作系统中的基本执行单元,它允许程序并发地...通过学习和理解这些源码,初学者可以深入理解DELPHI中的线程和信号量,掌握如何在多线程环境中实现有效的同步和互斥,这对于编写高效率、高并发的程序至关重要。

Global site tag (gtag.js) - Google Analytics