C 在使用带有信号处理程序的多线程程序时,有没有办法确保原子性?

C 在使用带有信号处理程序的多线程程序时,有没有办法确保原子性?,c,signals,posix,mutex,atomicity,C,Signals,Posix,Mutex,Atomicity,如果我有这样一个程序(伪代码): 然后,在尝试获取func中的锁时会出现死锁,而该锁已从“main”调用方获取。我想知道是否有一种方法可以暂停C语言中的信号处理,特别是在本例中,当输入func并获取锁时,恢复信号处理并在退出func时调用处理程序。您想要pthread\u sigmask,这是sigprocmask的多线程版本 下面是一些示例伪代码: int main(void) { sigset_t omask; sigset_t nmask; // add as m

如果我有这样一个程序(伪代码):


然后,在尝试获取func中的锁时会出现死锁,而该锁已从“main”调用方获取。我想知道是否有一种方法可以暂停C语言中的信号处理,特别是在本例中,当输入func并获取锁时,恢复信号处理并在退出func时调用处理程序。

您想要
pthread\u sigmask
,这是
sigprocmask
的多线程版本

下面是一些示例伪代码:

int
main(void)
{
    sigset_t omask;
    sigset_t nmask;

    // add as many signals as you want to the mask ...
    sigemptyset(&nmask);
    sigaddset(&nmask,SIGINT);

    // [temporarily] block signals
    pthread_sigmask(SIG_BLOCK,&nmask,&omask);

    // call function safely
    func();

    // restore signal mask
    pthread_sigmask(SIG_SETMASK,&omask,NULL);

    // pending signals should occur now ...
}
我不完全确定,但是,您可能需要使用
pthread\u sigmask
来阻止除一个线程外的所有线程中的信号,并仅从该线程执行上述操作

另外,如果我没有说我会重构你的代码,那我就是失职了。在信号处理程序中[除此之外]可以执行的操作数量是有限的(例如,no
malloc
、no
printf
,等等)

为信号处理指定一个线程,让它执行
sigsetjmp
,信号处理程序执行
siglongjmp

或者让信号处理程序设置一个易失性全局(例如,
信号\u发生
),在基本级别进行监控


因此,您在信号处理程序中执行的所有“繁重工作”都可以从基本任务级别完成,您可以在该级别执行任何操作。

您需要两个锁。一个用于
func()
内部,另一个用于保护进程的信号掩码

还必须对信号进行掩蔽和去掩蔽:

static  pthread_mutex_t mask_mutex = PTHREAD_MUTEX_INITIALIZER;
sigset_t old_set;
sigset_t new_set;

sigemptyset( &new_set );
sigaddset( &new_set, SIGINT );

pthread_mutex_lock( &mask_mutex );

pthread_sigmask( SIG_BLOCK, &new_mask, &old_mask );

func();

pthread_sigmask( SIG_SETMASK, &old_mask, NULL );

pthread_mutex_unlock( &mask_mutex );

如果
pthread_sigmask()
周围没有锁,线程可能会在执行重叠时损坏进程sigmask。

为了安全起见,您可以在这种未定义的情况下使用测试互斥函数(trylock)。有了这个,你不一定也需要阻塞。

我强烈建议不要将
siglongjmp()
插入另一个线程的
sigsetjmp()
创建的
sigjmp\u buf
中。但是,在pthread\u sigmask(SIG\u SETMASK,&omak,NULL)之后,我还会为挂起的信号调用信号处理程序吗??或者他们必须再次被呼叫??@EOF当然。我的第一行是关于在除了一个线程之外的所有线程中阻塞信号[这就是
sigsetjmp/siglongjmp
将要完成的地方]。我说:“专门用一个线程来处理信号,让它执行sigsetjmp,信号处理程序执行siglongjmp”。就我个人而言,我更喜欢(并且已经使用)volatile global(或等效的)方法。信号解除阻塞后,它们将发生,就像你一开始从未阻止过它们一样。因此,无需手动调用信号处理程序[无论如何,您都不能这样做,因为您无法设置特殊的信号处理程序上下文]。它将被调用[以信号处理程序总是被调用的特殊方式]。注意示例代码中的最后一行注释。除了互斥锁,
func
还在做什么?我为生产任务关键型实时系统做过类似的工作,我想再次强调,在C语言中,线程和信号处理之间的交互是未定义的,可能有一种更好的方式。你需要更具体一些。您的系统是POSIX系统吗?请相应地标记您的问题。如果在pthread\u sigmask(SIG\u块、&new\u掩码、&old\u掩码)之前和pthread\u mutex\u lock(&mask\u mutex)之后调用信号,该怎么办?那我们就陷入了僵局,对吗?如果我们切换操作顺序呢?@falhumai不,因为它不是同一个互斥体。如果在保护
pthread\u sigmask()
调用的互斥锁被锁定后发送信号,则锁定该互斥锁的线程仍无法在
func()
中保留互斥锁。在两个
pthread\u sigmask()
调用之间,
func()
实际上不是信号处理程序,因此可以安全地调用。第二个互斥锁用于保护
pthread\u sigmask()
调用,并为进程维护一致的信号掩码。
static  pthread_mutex_t mask_mutex = PTHREAD_MUTEX_INITIALIZER;
sigset_t old_set;
sigset_t new_set;

sigemptyset( &new_set );
sigaddset( &new_set, SIGINT );

pthread_mutex_lock( &mask_mutex );

pthread_sigmask( SIG_BLOCK, &new_mask, &old_mask );

func();

pthread_sigmask( SIG_SETMASK, &old_mask, NULL );

pthread_mutex_unlock( &mask_mutex );