如何唤醒睡眠(3)线程

时间:2016-07-26 10:33:48

标签: c linux pthreads signals

我想在主线程的另一个线程中中断sleep (3)。为此,我想在打破系统调用后使用raise (sig)并继续执行代码。

那么,我应该使用哪个信号来sleep并返回EINTR

到目前为止我编写的代码:

void *event_loop (void *threadid)
{
    int rc;
    long tid;
        do{
        rc = sleep(20);
        printf ("return from sleep with %d", rc);
        fprintf(stderr, "Value of errno: %d\n", errno);
        } while (errno == EINTR);
       tid = (long)threadid;
       printf("Hello World! It's me, thread #%ld! = %d\n", tid);

}

void  main(int argc, char *argv[])
{
   pthread_t threads[NUM_THREADS];
   int rc;
   long t;
   int sig=SIGABRT ; // which signal???
   int i=0;
   printf("Main THREAD\n");
   for(t=0;t<2;t++){
     printf("In main: creating thread %ld\n", t);
     rc = pthread_create(&threads[t], NULL, event_loop, (void *)t);
     if (rc){
       printf("ERROR; return code from pthread_create() is %d\n", rc);
       exit(-1);
       }
     }
    sleep(2);
    printf("rasing signal now from main thread...\n");
    raise(sig);
    pthread_exit(NULL);
}

3 个答案:

答案 0 :(得分:2)

这不是中断阻塞系统调用的信号;它是注册处理程序(未安装SA_RESTART标志)的信号的传递,导致阻塞系统调用中断。

这是一个例子。第一个函数是空信号处理程序(长格式,因此适用于POSIX信号,以及实时信号SIGRTMIN+0SIGRTMAX-1)。第二个功能是处理程序安装程序。

#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>

static void empty_handler(int signum, siginfo_t *info, void *context)
{
}

/* Install empty handler for the specified signal.
 * Returns 0 if success, errno error code otherwise.
*/ 
static int install_empty_handler(const int signum)
{
    struct sigaction  act;

    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);
    act.sa_sigaction = empty_handler;
    act.sa_flags = SA_SIGINFO;
    if (sigaction(signum, &act, NULL) == -1)
        return errno;

    return 0;
}

main()中,让我们说你跑

    if (install_empty_handler(SIGUSR1)) {
        fprintf(stderr, "Cannot install signal handler: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

在创建任何线程之前。 (始终为进程中的所有线程分配信号处理程序;您不能为一个线程设置一个处理程序,为另一个线程设置另一个处理程序。您可以检查已安装的处理程序中的当前线程ID,并根据它执行操作。所以,你在创建线程之前不需要安装它们;它只是在创建线程之前安装处理程序更容易维护,并且留下更少的竞争窗口。)

假设你有pthread_t somethread; somethread,其中pthread_kill(somethread, SIGUSR1); 包含阻塞系统调用中的线程的线程标识符。

如果您从任何线程致电

SIGUSR1

pthread_kill()信号被发送到该线程。

(请注意,empty_handler()调用将立即返回,您不能认为此时信号已经发送。如果您还想要发送通知,还需要一些其他机制。此外,信号是首先发送 ;阻塞系统调用仅在传递之后中断,如果系统负载很重,则两者之间可能存在可测量的延迟。通常没有,我只看到了几毫秒,但没有做出任何假设。如果你需要通知,让中断的系统调用在注意到它被中断后再执行。)

将信号传递到已安装的-1函数会导致同一线程中的阻塞系统调用中断。调用将失败,返回短计数(对于某些类型的套接字)或errno == EINTRraise(SIGUSR1)

如果您使用EINTR,内核可以在进程的线程中选择任何线程来处理信号。 (除了告诉内核他们希望通过pthread_sigmask()sigprocmask()阻止&#34;信号的线程,或者他们是否从创建该线程的线程继承了这样的块。) 无论如何,如果该线程处于阻塞系统调用中,它将被中断(由于传递信号);如果它正在做其他事情,那么该线程就是“借来的”#34;暂时没有中断的迹象。

这种机制有用的情况。特别是,我使用了超时定时器(使用timer_create(CLOCK_MONOTONIC, &event, &timer)timer_settime()来设置超时时的初始触发事件,然后在使用外部库时以每隔几毫秒的快速短时间间隔重复差/故障超时支持访问套接字。即使是一个糟糕的库也不会在最坏的情况下多次尝试阻塞读取,如果它们都因*错误而失败 - 当然,常见的典型情况是第一个强迫库立即报告错误。这是真正困扰我的角落案件;我只想发布一个不容易因为月亮碰巧处于错误阶段而过于敏感的强大代码。

答案 1 :(得分:1)

您可以拥有其他帖子blocked on a condition variable with a timeout

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <errno.h>

pthread_cond_t      cond  = PTHREAD_COND_INITIALIZER;
pthread_mutex_t     mutex = PTHREAD_MUTEX_INITIALIZER;
int interrupted;

#define WAIT_TIME_SECONDS 20
void *event_loop (void *arg)
{
    struct timespec   ts;
    struct timeval    tv;

again:
    gettimeofday(&tv, NULL);

    ts.tv_sec  = tv.tv_sec;
    ts.tv_nsec = tv.tv_usec * 1000;
    ts.tv_sec += WAIT_TIME_SECONDS;

    pthread_mutex_lock(&mutex);
    while (!interrupted) {
        int rc = pthread_cond_timedwait(&cond, &mutex, &ts);
        if (rc == ETIMEDOUT) {
            printf("Timed out!\n");
            pthread_mutex_unlock(&mutex);
            goto again;
        }
    }
    printf("Woke up!\n");
    interrupted = 0;
    pthread_mutex_unlock(&mutex);
    goto again;
}

int main(void) {
    pthread_t thread;
    pthread_create(&thread, NULL, event_loop, NULL);
    while (getchar() != EOF) {
        pthread_mutex_lock(&mutex);
        interrupted = 1;
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex);
    }
}

如果您希望信号由SIGINT等信号触发,请在专用线程中使用sigwait或使用Semaphores。条件变量不是异步信号安全的。

答案 2 :(得分:-1)

按Ctr + C时,操作系统向进程发送信号。这将发送SIGINT信号.SIGINT(&#34;程序中断&#34;)是终止信号之一.SIGINT是一个特殊的信号。信号,因为它可以由您的程序处理(捕获)。 SIGINT的默认操作是程序终止。要更改信号的默认操作,您必须注册要捕获的信号。要在C程序中注册信号(至少在POSIX系统下),有两个功能

  1. signal(int signum, sighandler_t handler);
  2. sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
  3. 这些函数要求标题signal.h包含在C代码中。

    示例:

    #include ...
    #include ...
    #include<signal.h>
    void handler(int sig){
     cout << "Interrupt signal (" << signum << ") received.\n";
    }
    int main()
    {
      //registering signal by signal function
      signal(SIGINIT,handler);
    
      //your code
    
    while(++i){
           cout << "Going to sleep...." << endl;
           if( i == 3 ){
              raise( SIGINT);
           }
           sleep(1);
        }
    //your code
    
      return 0;
    }