POSIX pthread编程

use*_*152 11 c posix pthreads

我必须编写一个多线程(比如2个线程)程序,其中每个线程执行不同的任务.此外,这些线程一旦启动就必须在后台无限运行.这就是我所做的.有人可以给我一些反馈,如果方法是好的,如果你看到一些问题.另外,我想知道如果用Ctrl + C终止执行,如何以系统的方式关闭线程.

main函数创建两个线程,让它们无限运行,如下所示.

这是骨架:

void    *func1();
void    *func2();

int main(int argc, char *argv[])
{   

    pthread_t th1,th2;
    pthread_create(&th1, NULL, func1, NULL);
    pthread_create(&th2, NULL, func2, NULL);

    fflush (stdout);
    for(;;){
    }
    exit(0); //never reached
}

void *func1()
{
    while(1){
    //do something
    }
}

void *func2()
{
    while(1){
    //do something
    }
} 
Run Code Online (Sandbox Code Playgroud)

谢谢.

使用答案中的输入编辑代码: 我是否正确退出线程?

#include <stdlib.h>     /*  exit() */
#include <stdio.h>      /* standard in and output*/
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <signal.h>
#include <semaphore.h>

sem_t end;

void    *func1();
void    *func2();

void ThreadTermHandler(int signo){
    if (signo == SIGINT) {
        printf("Ctrl+C detected !!! \n");
        sem_post(&end);
        }
}
void *func1()
{
    int value;
    for(;;){
        sem_getvalue(&end, &value);
        while(!value){
            printf("in thread 1 \n");
        }
    }
    return 0;
}

void *func2()
{
    int value;
    for(;;){
        sem_getvalue(&end, &value);
        while(!value){
            printf("value = %d\n", value);
        }
    }
    return 0;
}

int main(int argc, char *argv[])
{


    sem_init(&end, 0, 0);
    pthread_t th1,th2;
    int value  = -2;
    pthread_create(&th1, NULL, func1, NULL);
    pthread_create(&th2, NULL, func2, NULL);

    struct sigaction sa;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = ThreadTermHandler;
    // Establish a handler to catch CTRL+c and use it for exiting.
    if (sigaction(SIGINT, &sa, NULL) == -1) {
        perror("sigaction for Thread Termination failed");
        exit( EXIT_FAILURE );
    }

    /* Wait for SIGINT. */
    while (sem_wait(&end)!=0){}
    //{
        printf("Terminating Threads.. \n");
        sem_post(&end);
                sem_getvalue(&end, &value);
        /* SIGINT received, cancel threads. */
        pthread_cancel(th1);
        pthread_cancel(th2);
        /* Join threads. */
        pthread_join(th1, NULL);
        pthread_join(th2, NULL);
    //}
    exit(0);
}
Run Code Online (Sandbox Code Playgroud)

ale*_*cov 13

线程终止主要有两种方法.

  • 使用取消点.线程将在请求取消时终止,并且到达取消点,从而以受控方式结束执行;
  • 使用信号.让线程安装一个信号处理程序,它提供终止机制(设置标志并作出反应EINTR).

两种方法都有警告.有关更多详细信息,请参阅Pthread库中的Kill Thread.

在您的情况下,似乎是一个使用取消点的好机会.我将使用一个评论的例子.为清楚起见,省略了错误检查.

#define _POSIX_C_SOURCE 200809L
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void sigint(int signo) {
    (void)signo;
}

void *thread(void *argument) {
    (void)argument;
    for (;;) {
        // Do something useful.
        printf("Thread %u running.\n", *(unsigned int*)argument);

        // sleep() is a cancellation point in this example.
        sleep(1);
    }
    return NULL;
}

int main(void) {
    // Block the SIGINT signal. The threads will inherit the signal mask.
    // This will avoid them catching SIGINT instead of this thread.
    sigset_t sigset, oldset;
    sigemptyset(&sigset);
    sigaddset(&sigset, SIGINT);
    pthread_sigmask(SIG_BLOCK, &sigset, &oldset);

    // Spawn the two threads.
    pthread_t thread1, thread2;
    pthread_create(&thread1, NULL, thread, &(unsigned int){1});
    pthread_create(&thread2, NULL, thread, &(unsigned int){2});

    // Install the signal handler for SIGINT.
    struct sigaction s;
    s.sa_handler = sigint;
    sigemptyset(&s.sa_mask);
    s.sa_flags = 0;
    sigaction(SIGINT, &s, NULL);

    // Restore the old signal mask only for this thread.
    pthread_sigmask(SIG_SETMASK, &oldset, NULL);

    // Wait for SIGINT to arrive.
    pause();

    // Cancel both threads.
    pthread_cancel(thread1);
    pthread_cancel(thread2);

    // Join both threads.
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    // Done.
    puts("Terminated.");
    return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

阻塞/解除阻塞信号的需求是,如果您将SIGINT发送到进程,任何线程都可以捕获它.您在生成线程之前这样做,以避免让它们自己执行并需要与父级同步.创建线程后,还原掩码并安装处理程序.

如果线程分配了大量资源,取消点可能会很棘手; 在这种情况下,你将不得不使用pthread_cleanup_push()pthread_cleanup_pop(),这是一个烂摊子.但如果使用得当,这种方法是可行的,相当优雅.


R..*_*R.. 7

答案很大程度上取决于用户按下时想要做什么CtrlC.

如果您的工作线程没有修改退出时需要保存的数据,则无需执行任何操作.默认操作SIGINT是终止进程,包括构成进程的所有线程.

但是,如果您的线程确实需要执行清理,那么您还有一些工作要做.您需要考虑两个单独的问题:

  • 如何处理信号并将消息传递给需要终止的线程.
  • 线程如何接收和处理终止请求.

首先,信号处理程序是一种痛苦.除非你非常小心,否则你必须假设从信号处理程序调用大多数库函数是不合法的.幸运的是,sem_post指定为异步信号安全,并且可以完美地满足您的要求:

  1. 在程序开始时,使用初始化信号量 sem_init(&exit_sem, 0, 0);
  2. 为执行和返回的SIGINT(以及您想要处理的任何其他终止信号)安装信号处理程序.SIGTERMsem_post(&exit_sem);
  3. 用.替换for(;;);主线程while (sem_wait(&exit_sem)!=0).
  4. sem_wait成功之后,主线程应该通知所有其他线程它们应该退出,然后等待它们全部退出.

以上也可以在没有使用信号掩码的信号量的情况下完成sigwaitinfo,但我更喜欢信号量方法,因为它不需要你学习很多复杂的信号语义.

现在,有几种方法可以处理通知工作线程是时候退出了.我看到的一些选项:

  1. 让它们sem_getvalue(&exit_sem)定期检查并清理并退出,如果它返回非零值.但请注意,如果线程被无限期阻塞,例如在调用read或中,则无法执行此操作write.
  2. 使用pthread_cancel,并小心地放置取消处理程序(pthread_cleanup_push)到处.
  3. 使用pthread_cancel,但也可用于pthread_setcancelstate在大多数代码中禁用取消,并且仅在您要执行阻止IO操作时重新启用它.这样,您只需要将清理处理程序放在启用取消的位置.
  4. 学习高级信号语义,并设置一个额外的信号和中断信号处理程序,你发送到所有线程,pthread_kill这将导致阻塞系统调用返回EINTR错误.然后你的线程可以对此采取行动并通过一系列故障退出正常的C方式,一直返回start函数.

我不建议初学者使用方法4,因为很难做到正确,但对于高级C程序员来说,它可能是最好的,因为它允许您使用现有的C语言,通过返回值而不是"例外"来报告异常条件.

另请注意,如果您没有呼叫任何其他取消点功能pthread_cancel,您将需要定期呼叫.否则,取消请求将永远不会被采取行动.pthread_testcancel