【问题标题】:How to wake up sleep(3)ing thread [closed]如何唤醒睡眠(3)线程[关闭]
【发布时间】:2016-07-26 10:33:47
【问题描述】:

我想在我的主线程的另一个线程中打破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);
}

【问题讨论】:

  • 经典XY problem。你到底想做什么?
  • 我附加的程序应该是解释,我尝试通过raise()生成EINTR
  • 我改写了你的问题,所以它传达了你想要解决的实际问题。
  • 你不需要信号,信号和线程不能很好地满足你的需求。而是在谷歌上搜索pthread_cond_wait()。

标签: c linux pthreads signals


【解决方案1】:

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

这是一个例子。第一个函数是一个空信号处理程序(长格式,因此既适用于 POSIX 信号,也适用于实时信号 SIGRTMIN+0 到 SIGRTMAX-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() 调用将立即返回,并且您不能假设此时信号已被传递。如果您还需要传递通知,则需要一些其他机制。此外,信号已传递 first;阻塞系统调用仅在交付后被中断,如果系统负载较重,两者之间可能会有可测量的延迟。通常没有,我我们只看到毫秒,但没有对此做任何假设。如果您需要通知,请让被中断的系统调用在它注意到它被中断后执行它。)

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

如果您改为使用raise(SIGUSR1),内核可以在进程的线程中选择任何线程来处理信号。 (除了那些告诉内核他们希望通过pthread_sigmask() 或sigprocmask() 来“阻止”信号的线程,或者如果他们从创建该线程的线程继承了这样的块。) 无论如何,如果该线程处于阻塞系统调用中,它将被中断(由于信号的传递);如果是在做别的事情,线程只是“借”了一会儿,真的没有中断的迹象。


在种情况下这种机制很有用。特别是,当使用具有不良/故障超时的外部库时,我将它与超时计时器一起使用(使用timer_create(CLOCK_MONOTONIC, &amp;event, &amp;timer) 和timer_settime() 在超时时设置初始触发事件,然后以快速的短间隔重复,每隔几毫秒)支持访问套接字。即使是一个糟糕的库在最坏的情况下也不会尝试多次阻塞读取,如果它们都因EINTR 错误而失败 - 当然,常见的典型情况是第一个强制库立即报告错误。真正困扰我的是极端案例。我只想发布健壮的代码,不会因为月亮碰巧处于错误的相位而导致崩溃。

【讨论】:

    【解决方案2】:

    你可以有其他线程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。条件变量不是异步信号安全的。

    【讨论】:

      【解决方案3】:

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

      1. signal(int signum, sighandler_t handler);
      2. sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

      这些函数需要将头文件 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;
      }
      

      【讨论】:

      • 我不想使用处理程序。我想引起中断来破坏不同线程中的系统调用并像这样检查它: if (errno == EINTR) {..restart系统调用...}
      • 你可以使用SIGABRT信号
      • 没用,我得到“中止(核心转储)”我附加了我的程序,我希望现在我的问题会更清楚。
      • 问题中没有c++标签。
      猜你喜欢
      • 2014-07-29
      • 1970-01-01
      • 2011-06-01
      • 2018-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-29
      • 1970-01-01
      相关资源
      最近更新 更多