gpt4 book ai didi

c - 如何使用 posix 信号正确挂起多个线程?

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:39:06 27 4
gpt4 key购买 nike

在现有多线程应用程序的上下文中,我想在特定持续时间内暂停线程列表,然后恢复它们的正常执行。我知道你们中的一些人会说我不应该那样做,但我知道这一点而且我别无选择。

我想出了以下代码,该代码可以正常工作但随机失败。对于我想挂起的每个线程,我发送一个信号并通过信号量等待确认。信号处理程序在被调用时发布信号量并在指定的持续时间内休眠。

问题是当系统完全加载时,对 sem_timedwait 的调用有时会因 ETIMEDOUT 而失败,我会留下一个不一致的逻辑,信号量用于 ack:我不知道信号是否已被丢弃或只是晚了。

// compiled with: gcc main.c -o test -pthread

#include <pthread.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/syscall.h>

#define NUMTHREADS 40
#define SUSPEND_SIG (SIGRTMIN+1)
#define SUSPEND_DURATION 80 // in ms

static sem_t sem;

void checkResults(const char *msg, int rc) {
if (rc == 0) {
//printf("%s success\n", msg);
} else if (rc == ESRCH) {
printf("%s failed with ESRCH\n", msg);
} else if (rc == EINVAL) {
printf("%s failed with EINVAL\n", msg);
} else {
printf("%s failed with unknown error: %d\n", msg, rc);
}
}

static void suspend_handler(int signo) {
sem_post(&sem);
usleep(SUSPEND_DURATION*1000);
}

void installSuspendHandler() {
struct sigaction sa;

memset(&sa, 0, sizeof(sa));

sigemptyset(&sa.sa_mask);

sa.sa_flags = 0;
sa.sa_handler = suspend_handler;

int rc = sigaction(SUSPEND_SIG, &sa, NULL);
checkResults("sigaction SUSPEND", rc);
}

void *threadfunc(void *param) {
int tid = *((int *) param);
free(param);

printf("Thread %d entered\n", tid);

// this is an example workload, the real app is doing many things
while (1) {
int rc = sleep(30);

if (rc != 0 && errno == EINTR) {
//printf("Thread %d got a signal delivered to it\n", tid);
} else {
//printf("Thread %d did not get expected results! rc=%d, errno=%d\n", tid, rc, errno);
}
}

return NULL;
}

int main(int argc, char **argv) {
pthread_t threads[NUMTHREADS];
int i;

sem_init(&sem, 0, 0);

installSuspendHandler();

for(i=0; i<NUMTHREADS; ++i) {
int *arg = malloc(sizeof(*arg));
if ( arg == NULL ) {
fprintf(stderr, "Couldn't allocate memory for thread arg.\n");
exit(EXIT_FAILURE);
}

*arg = i;
int rc = pthread_create(&threads[i], NULL, threadfunc, arg);
checkResults("pthread_create()", rc);
}

sleep(3);

printf("Will start to send signals...\n");

while (1) {
printf("***********************************************\n");
for(i=0; i<NUMTHREADS; ++i) {
int rc = pthread_kill(threads[i], SUSPEND_SIG);
checkResults("pthread_kill()", rc);

printf("Waiting for Semaphore for thread %d ...\n", i);

// compute timeout abs timestamp for ack
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
const int TIMEOUT = SUSPEND_DURATION*1000*1000; // in nano-seconds

ts.tv_nsec += TIMEOUT; // timeout to receive ack from signal handler

// normalize timespec
ts.tv_sec += ts.tv_nsec / 1000000000;
ts.tv_nsec %= 1000000000;

rc = sem_timedwait(&sem, &ts); // try decrement semaphore

if (rc == -1 && errno == ETIMEDOUT) {
// timeout
// semaphore is out of sync
printf("Did not received signal handler sem_post before timeout of %d ms for thread %d", TIMEOUT/1000000, i);
abort();
}
checkResults("sem_timedwait", rc);
printf("Received Semaphore for thread %d.\n", i);
}

sleep(1);
}

for(i=0; i<NUMTHREADS; ++i) {
int rc = pthread_join(threads[i], NULL);
checkResults("pthread_join()\n", rc);
}
printf("Main completed\n");
return 0;
}

有问题吗?

  • 信号是否有可能被丢弃并且永远不会传送?
  • 系统加载时信号量随机超时的原因是什么?

最佳答案

usleep() 不在异步信号安全函数中(尽管 sleep() 是,并且还有其他异步信号安全函数,您可以使用它们可以产生定时延迟)。因此,从信号处理程序调用 usleep() 的程序是不符合规范的。规范没有描述可能发生的事情——既没有这样的调用本身,也没有描述它发生的更大的程序执行。只有合格程序才能回答您的问题;我在下面这样做。


  • Is it possible for a signal to be dropped and never delivered?

这取决于你的意思:

  • 如果将正常(非实时)信号传送到已将该信号排队的线程,则不会有其他实例排队。

  • 一个线程可以在信号还在等待它的时候死掉;这些信号将不会被处理。

  • 线程可以更改给定信号的配置(例如,更改为 SIG_IGN),尽管这是每个进程的属性,而不是每个线程的属性。

  • 一个线程可以无限期地阻塞一个信号。阻塞的信号不会被丢弃——它会保持在线程队列中,并且最终会在解除阻塞后的某个时间被接收到(如果发生这种情况的话)。

但不会,通过 kill()raise() 函数成功排队信号后,该信号不会被随机丢弃。

  • What causes the timeout on the semaphore at random time when the system is loaded?

一个线程只有当它实际运行在一个核上时才能接收到一个信号。在可运行进程多于内核的系统上,某些可运行进程必须在任何给定时间挂起,在任何内核上都没有时间片。在重载 系统上,这是常态。信号是异步的,因此您可以将一个信号发送到当前正在等待时间片的线程,而发送者不会阻塞。那么,您发出信号的线程完全有可能在超时到期之前没有被安排运行。如果它确实运行了,它可能由于某种原因而阻塞了信号,并且在它用完它的时间片之前没有抽出时间解除阻塞。


最终,您可以使用基于信号量的方法检查目标线程是否在您选择的任何超时内处理了信号,但您无法提前预测线程处理信号需要多长时间,甚至也无法预测它是否会在任何有限的时间内这样做(例如,它可能会在这样做之前因某种原因死亡)。

关于c - 如何使用 posix 信号正确挂起多个线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44949228/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com