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

C 如何使用posix信号正确挂起多个线程?,c,linux,multithreading,pthreads,signals,C,Linux,Multithreading,Pthreads,Signals,在现有多线程应用程序的上下文中,我希望在特定的持续时间内暂停线程列表,然后恢复其正常执行。我知道你们中的一些人会说我不应该那样做,但我知道,我没有选择 我想出了下面的代码,这类工作,但随机失败。对于我想要挂起的每个线程,我发送一个信号并通过信号量等待确认。调用信号处理程序时,在指定的持续时间内发布信号量并休眠 问题是,当系统完全加载时,对sem_timedwait的调用有时会因ETIMEDOUT而失败,我的逻辑与用于ack的信号量不一致:我不知道信号是否已丢弃或只是延迟 // compiled

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

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

问题是,当系统完全加载时,对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;
}
//编译时使用:gcc main.c-o test-pthread
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义NUMTHREADS 40
#定义SUSPEND_SIG(SIGRTMIN+1)
#定义挂起持续时间80//(毫秒)
静态扫描电镜;
无效检查结果(const char*msg,int rc){
如果(rc==0){
//printf(“%s成功\n”,msg);
}else if(rc==ESRCH){
printf(“%s失败,ESRCH\n”,msg);
}else if(rc==EINVAL){
printf(“%s因EINVAL而失败,\n”,msg);
}否则{
printf(“%s失败,错误未知:%d\n”,msg,rc);
}
}
静态无效挂起\u处理程序(int signo){
sem_post(和sem);
usleep(暂停时间*1000);
}
void installSuspendHandler(){
struct-sigaction-sa;
memset(&sa,0,sizeof(sa));
sigemptyset(和sa.sa_面具);
sa.sa_标志=0;
sa.sa_handler=suspend_handler;
int rc=sigaction(SUSPEND_SIG,&sa,NULL);
检查结果(“sigaction SUSPEND”,rc);
}
void*threadfunc(void*param){
int-tid=*((int*)参数);
自由(param);
printf(“输入线程%d\n”,tid);
//这是一个示例工作负载,真正的应用程序正在做很多事情
而(1){
int rc=睡眠(30);
如果(rc!=0&&errno==EINTR){
//printf(“线程%d收到了一个信号,\n”,tid);
}否则{
//printf(“线程%d未获得预期结果!rc=%d,errno=%d\n”,tid,rc,errno);
}
}
返回NULL;
}
int main(int argc,字符**argv){
pthread_t threads[NUMTHREADS];
int i;
sem_init(&sem,0,0);
installSuspendHandler();
for(i=0;i
usleep()
不在异步信号安全函数之列(虽然
sleep()
在异步信号安全函数之列,并且还有其他异步信号安全函数可用于产生定时延迟)。调用
usleep()的程序因此,来自信号处理程序的
是不符合要求的。规范没有描述可能发生的情况——无论是这种调用本身,还是它发生的更大的程序执行。您的问题只能针对符合要求的程序来回答;我在下面这样做


  • 信号是否可能被丢弃而永远无法发送
这取决于你的确切意思:

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

  • 线程可能在信号仍在排队等待的情况下死亡;这些信号将不会被处理

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

  • 线程可以无限期地阻止一个信号。被阻止的信号不会被丢弃——它会一直排队等待线程,如果发生这种情况,它最终会在解除阻止后一段时间被接收

但是,如果通过
kill()
raise()
函数成功地将信号排队,则不会随机丢弃该信号

  • 当系统加载时,是什么导致信号灯在随机时间超时
线程只有在实际运行在内核上时才能接收信号。在可运行进程多于内核的系统上,某些可运行进程在任何给定时间都必须挂起,而在任何内核上都没有时间片。在负载较重的系统上,这是正常现象。信号是异步的,因此可以将一个信号发送到当前正在运行的线程正在等待没有发送方阻止的时间片。因此,您发出信号的线程完全可能在超时过期之前未计划运行。如果它确实运行,则可能由于某种原因阻止了信号,并且在用完其时间片之前未着手解除阻止



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

我知道usleep不是异步信号安全的,但睡眠是,我也有同样的睡眠问题。我运行了其他测试,似乎信号处理程序可能需要几秒钟才能执行,而且它只发生在SD卡上执行io的线程上。SD卡上的io会延迟信号处理吗?@GuillaumeMICHEL这完全是编造的在SD卡上执行I/O的线程在执行期间有效地阻止了信号。然而,我会惊讶地发现发送给这样一个线程的信号丢失了——这将是不一致的。