何时使用pthread_exit()以及何时在Linux中使用pthread_join()?

时间:2013-12-29 10:35:45

标签: c linux pthreads

我是pthreads的新手,我正在努力理解它。我看到了一些如下例子。

我可以看到main()已被API pthread_exit()阻止,我看到了API pthread_join()阻止主要功能的示例。我无法理解何时使用什么?

我指的是以下网站 - https://computing.llnl.gov/tutorials/pthreads/。我无法理解何时使用pthread_join()以及何时使用pthread_exit()

有人可以解释一下吗?此外,pthreads的良好教程链接将不胜感激。

#include <pthread.h>
#include <stdio.h>
#define NUM_THREADS     5

void *PrintHello(void *threadid)
{
   long tid;
   tid = (long)threadid;
   printf("Hello World! It's me, thread #%ld!\n", tid);
   pthread_exit(NULL);
}

int main (int argc, char *argv[])
{
   pthread_t threads[NUM_THREADS];
   int rc;
   long t;
   for(t=0; t<NUM_THREADS; t++){
      printf("In main: creating thread %ld\n", t);
      rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
      if (rc){
         printf("ERROR; return code from pthread_create() is %d\n", rc);
         exit(-1);
      }
   }

   /* Last thing that main() should do */
   pthread_exit(NULL);

实现了另外一件事,即

pthread_cancel(thread);
pthread_join(thread, NULL);

有时,您希望在执行时取消该线程。 你可以使用pthread_cancel(thread);来完成这个。 但是,请记住,您需要启用pthread取消支持。 此外,取消时清理代码。

thread_cleanup_push(my_thread_cleanup_handler, resources);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0);

static void my_thread_cleanup_handler(void *arg)
{
  // free
  // close, fclose
}

10 个答案:

答案 0 :(得分:39)

如openpub文档中所述,

pthread_exit()将退出调用它的线程。

在你的主要调用它的情况下,主线程将终止,而你的衍生线程将继续执行。这主要用于以下情况 主线程只需要生成线程并让线程完成它们的工作

pthread_join 除非目标线程终止,否则将暂停执行已调用它的线程

如果您希望在进一步之前等待thread / s终止,这非常有用 在主线程中处理。

答案 1 :(得分:13)

pthread_exit终止调用线程,而pthread_join暂停执行调用线程,直到目标线程完成执行。

在开放组文档中对它们进行了详细解释:

答案 2 :(得分:8)

这两种方法都可确保您的进程在所有线程结束之前不会结束。

join方法使main函数的线程明确等待所有要“加入”的线程。

pthread_exit方法以受控方式终止您的main函数和线程。 main具有结尾main的特殊性,否则将终止整个过程,包括所有其他线程。

为了使其正常工作,您必须确保没有任何线程使用在其main函数内声明的局部变量。该方法的优点是你的main不必知道你的进程中已经启动的所有线程,例如因为其他线程自己创建了main不了解的新线程

答案 3 :(得分:2)

您不需要在特定代码中调用pthread_exit(3)

一般情况下,main帖子调用pthread_exit,但应经常致电pthread_join(3) 等待等其他人线程完成。

PrintHello函数中,您无需调用pthread_exit,因为它在返回后是隐式的。

所以你的代码应该是:

void *PrintHello(void *threadid)  {
  long tid = (long)threadid;
  printf("Hello World! It's me, thread #%ld!\n", tid);
  return threadid;
}

int main (int argc, char *argv[]) {
   pthread_t threads[NUM_THREADS];
   int rc;
   intptr_t t;
   // create all the threads
   for(t=0; t<NUM_THREADS; t++){
     printf("In main: creating thread %ld\n", (long) t);
     rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
     if (rc) { fprintf(stderr, "failed to create thread #%ld - %s\n",
                                (long)t, strerror(rc));
               exit(EXIT_FAILURE);
             };
   }
   pthread_yield(); // useful to give other threads more chance to run
   // join all the threads
   for(t=0; t<NUM_THREADS; t++){
      printf("In main: joining thread #%ld\n", (long) t);
      rc = pthread_join(&threads[t], NULL);
      if (rc) { fprintf(stderr, "failed to join thread #%ld - %s\n",
                                (long)t, strerror(rc));
               exit(EXIT_FAILURE);
      }
   }
}

答案 4 :(得分:2)

pthread_exit()API

已经说过,用于调用线程终止。 在调用该函数后,启动复杂的清理机制。 完成后,线程终止。 当在pthread_create()创建的线程中发生对return()例程的调用时,也会隐式调用pthread_exit()API。 实际上,调用return()和调用pthread_exit()会产生相同的影响,从pthread_create()创建的线程调用。

区分在main()函数启动时隐式创建的初始线程和pthread_create()创建的线程非常重要。 从main()函数调用return()例程会隐式调用exit()系统调用,整个过程终止。 没有启动线程清理机制。 从main()函数调用pthread_exit()会导致清理机制启动,当它完成其工作时,初始线程终止。

从main()函数调用pthread_exit()时,整个进程(以及其他线程)会发生什么情况取决于PTHREAD实现。 例如,在IBM OS / 400实现中,当从main()函数调用pthread_exit()时,整个过程将终止,包括其他线程。 其他系统可能表现不同。 在大多数现代Linux机器上,从初始线程调用pthread_exit()不会终止整个进程,直到所有线程终止。 如果要编写便携式应用程序,请小心使用main()中的pthread_exit()。

pthread_join()API

是等待线程终止的便捷方式。 您可以编写自己的函数来等待线程终止,可能更适合您的应用程序,而不是使用pthread_join()。 例如,它可以是基于等待条件变量的函数。

我建议您阅读一本 David R. Butenhof“用POSIX线程编程”的书。 它很好地解释了所讨论的主题(以及更复杂的事情)(尽管一些实现细节,例如主函数中的pthread_exit用法,并不总是反映在书中)。

答案 5 :(得分:2)

pthread_exit()将终止调用线程并从中退出(但调用线程使用的资源如果未与主线程分离则不会释放到操作系统。)

pthrade_join()将等待或阻止调用线程,直到目标线程未终止。 简单来说,它将等待退出目标线程。

在你的代码中,如果你在PrintHello之前的pthread_exit()函数中放入睡眠(或延迟),那么主线程可能会退出并终止完整进程,尽管你的PrintHello函数不是完成后它将终止。如果在main中调用pthrade_join()之前在main中使用pthread_exit()函数,它将阻塞主线程并等待完成调用线程(PrintHello)。

答案 6 :(得分:1)

嗯。

来自http://pubs.opengroup.org/onlinepubs/009604599/functions/pthread_exit.html

POSIX pthread_exit说明:

After a thread has terminated, the result of access to local (auto) variables of the thread is 
undefined. Thus, references to local variables of the exiting thread should not be used for 
the pthread_exit() value_ptr parameter value.

这似乎与本地main()线程变量仍然可访问的想法相反。

答案 7 :(得分:0)

在主线程中使用pthread_exit(代替pthread_join),将使主线程处于defunct(僵尸)状态。由于未使用pthread_join,因此终止的其他可连接线程也将保持僵尸状态,并导致资源泄漏

  

无法与可连接的线程(即   (未分离),产生“僵尸线”。避免这样做,因为   每个僵尸线程都会消耗一些系统资源,并且在足够的时间   僵尸线程已积累,将不再可能   创建新的线程(或进程)。

另一点是使主线程保持关闭状态,而其他线程正在运行时,则可能在各种情况下引起与实现相关的问题,例如是否在主线程中分配了资源或在其他线程中使用了主线程本地的变量

此外,所有共享资源仅在进程退出时才释放,它不保存任何资源。因此,我认为应避免使用pthread_exit代替pthread_join

答案 8 :(得分:0)

当调用pthread_exit()时,调用线程堆栈不再可寻址为任何其他线程的“活动”内存。 “静态”内存分配的.data,.text和.bss部分仍可用于所有其他线程。因此,如果需要将一些内存值传递给pthread_exit()以供其他pthread_join()调用者查看,则调用pthread_join()的线程必须“可用”才能使用。应该为它分配malloc()/ new,在pthread_join线程堆栈上分配,1)pthread_join调用者传递给pthread_create的堆栈值,或者以其他方式使该值可用于调用pthread_exit()的线程,或者2)分配的静态.bss值。

弄清如何在线程堆栈之间管理内存以及将值存储在用于存储进程范围值的.data / .bss内存部分中,至关重要。

答案 9 :(得分:0)

  #include<stdio.h>
  #include<pthread.h>
  #include<semaphore.h>
 
   sem_t st;
   void *fun_t(void *arg);
   void *fun_t(void *arg)
   {
       printf("Linux\n");
       sem_post(&st);
       //pthread_exit("Bye"); 
       while(1);
       pthread_exit("Bye");
   }
   int main()
   {
       pthread_t pt;
       void *res_t;
       if(pthread_create(&pt,NULL,fun_t,NULL) == -1)
           perror("pthread_create");
       if(sem_init(&st,0,0) != 0)
           perror("sem_init");
       if(sem_wait(&st) != 0)
           perror("sem_wait");
       printf("Sanoundry\n");
       //Try commenting out join here.
       if(pthread_join(pt,&res_t) == -1)
           perror("pthread_join");
       if(sem_destroy(&st) != 0)
           perror("sem_destroy");
       return 0;
   }

将此代码复制并粘贴到gdb上。 Onlinegdb可以正常工作,并且自己看看。

确保您了解创建线程后,该进程与main同时运行。

  1. 没有联接,主线程继续运行并返回0
  2. 通过连接,主线程将被困在while循环中,因为它等待线程完成执行。
  3. 通过加入并删除注释掉的pthread_exit,线程将在运行while循环之前终止,并且main可以继续
  4. pthread_exit的实际用法可以用作if条件或case语句,以确保退出之前运行某些版本的代码。
void *fun_t(void *arg)
   {
       printf("Linux\n");
       sem_post(&st); 
       if(2-1 == 1)  
           pthread_exit("Bye");
       else
       { 
           printf("We have a problem. Computer is bugged");
           pthread_exit("Bye"); //This is redundant since the thread will exit at the end
                                //of scope. But there are instances where you have a bunch
                                //of else if here.
       }
   }

在此示例中,我想演示有时候需要使用信号量首先运行一段代码。

#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>

sem_t st;

void* fun_t (void* arg)
{
    printf("I'm thread\n");
    sem_post(&st);
}

int main()
{
    pthread_t pt;
    pthread_create(&pt,NULL,fun_t,NULL);
    sem_init(&st,0,0);
    sem_wait(&st);
    printf("before_thread\n");
    pthread_join(pt,NULL);
    printf("After_thread\n");
    
}

说明了fun_t在“线程之前”之后的运行方式如果期望的输出从上到下是线性的,则预期的输出将在线程之前,即线程之后。但是在这种情况下,我们将阻止main继续运行,直到func_t释放了信号量为止。可以使用https://www.onlinegdb.com/

验证结果