【问题标题】:pthread_cond_wait returns after sign_handler()?pthread_cond_wait 在 sign_handler() 之后返回?
【发布时间】:2020-06-11 17:34:42
【问题描述】:

各位程序员。

如果我向卡在 pthread_cond_wait() 上的线程发送 SIGINT 信号,当 sign_handler() 返回时,pthread_cond_wait() 是否也会返回?

如果没有,有没有办法让 pthread_cond_wait() 返回?

【问题讨论】:

  • 1.不。2。不。你为什么要这样做?无论如何都不可能以无比赛的方式使用。

标签: c linux multithreading synchronization conditional-statements


【解决方案1】:

如果我向卡在 pthread_cond_wait() 上的线程发送 SIGINT 信号,当 sign_handler() 返回时,pthread_cond_wait() 是否也会返回?

没有。

如果没有,有什么方法可以让 pthread_cond_wait() 返回?

不,您试图使用错误的工具来解决您遇到的任何潜在问题。

(从技术上讲,pthread_cond_timedwait()允许在被信号传递中断时返回,但它不会这样做,至少在运行内核 5.3.0 的 x86-64 上使用 GNU glibc 2.27 时. 是的,我查过了。)

如何解决我的问题?

让我们假设条件变量是您的用例的最佳选择。 (不过,这只是一个猜测;您没有告诉我们您要解决的真正问题,只是您选择的解决方案如何不起作用。)

然后,推荐的解决方案是使用辅助线程来捕获信号,例如 SIGINT,使用 sigwaitinfo() 或 sigtimedwait()。然后,该帮助线程可以设置特定的volatile sig_atomic_t you_need_to_exit 标志,并在相关条件变量上设置pthread_cond_signal()pthread_cond_broadcast(),让他们知道发生了重要的事情。那些等待条件变量的人显然应该首先检查辅助标志;如果设置,则假设这是唤醒信号的来源。通常我将这些标志命名为need_to_exit 或类似名称。

这种信号处理辅助线程的关键是信号需要在所有线程中被阻塞(包括处理辅助线程本身)。最好在创建任何其他线程之前在主线程中执行此操作,因为创建的线程会继承相同的信号掩码。

siginfo_t 结构包含各种有用的信息。最有用的可能是.si_pid 字段,它告诉哪个进程(或0,如果是内核)发送了信号。这样,如果您出于内部目的使用 SIGRTMIN+0SIGRTMAX-0 信号,则可以忽略它们,除非它们来自进程本身(其他线程,.si_pid == getpid())。

线程取消(延迟,在取消点;pthread_cond_wait() 是取消点)是另一种选择。如果线程被取消,您可以使用 pthread_cleanup_push() 设置/添加要运行的函数。这基本上是强行杀死目标线程,但它可以运行它在死亡之前设置的任何清理功能。您还可以使用 pthread_cleanup_pop() 解散任何清理函数——一个参数指定清理函数是运行并丢弃,还是仅仅丢弃。但是,当取消时,线程总是会死掉。


请使用pthread_attr_t 将堆栈大小限制为2 的合理幂。如果堆栈上没有任何大型数组或结构(局部变量),那么类似

#include <limits.h>

#ifndef  THREAD_STACK_SIZE
#define  THREAD_STACK_SIZE  (4 * PTHREAD_STACK_MIN)
#endif

sigset_t        mask;
pthread_attr_t  attrs;
int             err;

/* Block SIGINT in this (and all created threads) */
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
err = pthread_sigmask(SIG_BLOCK, &mask, NULL);
if (err) {
    fprintf(stderr, "Cannot block signals: %s.\n", strerror(err));
    return EXIT_FAILURE;
}

/* Create stack size attribute. */
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, THREAD_STACK_SIZE);

/*
 * Create threads, use &attrs for the second parameter.
*/

/* Optional cleanup - it's a good idea to be careful. */
pthread_attr_destroy(&attrs);

对于堆栈大小和阻塞所有线程中的某些信号来说应该可以正常工作(通过首先在创建其他线程的线程中阻塞它们;它们将继承信号掩码)。

您可以将任何其他信号添加到您喜欢的屏蔽掩码中,但 SIGKILLSIGSTOP 不能被屏蔽、捕获或忽略。

pthread_create() 的第二个参数pthread_attr_t 只是设置(或属性)的集合,例如配置;它们不会被 pthread_create() 调用“消耗”。您可以多次使用同一组属性。这仅包含所需的堆栈大小。 (它不包含堆栈本身,只包含所需的大小。)

默认堆栈大小非常大,通常为 8 MiB,这意味着大量虚拟内存毫无理由地保留给线程堆栈,真的。此外,它严重限制了进程可以创建的线程数。

在许多方面,拥有一个用于信号处理的辅助线程比实际的信号处理程序更容易,因为只有async-signal safe functions 在信号处理程序中使用是安全的;而在辅助线程中,您可以使用 all。

【讨论】:

  • 这很有帮助。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-03
  • 2012-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多