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

2024-11-01 08:41:00
admin
原创
54
摘要:问题描述:我是 pthreads 的新手,正在尝试理解它。我看到了一些类似以下的示例。我可以看到被main()API 阻止了pthread_exit(),并且我也看到了主函数被 API 阻止的示例pthread_join()。我不明白何时使用什么?我指的是以下网站 - https://computing.ll...

问题描述:

我是 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!
", 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
", t);
      rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
      if (rc){
         printf("ERROR; return code from pthread_create() is %d
", 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
}

解决方案 1:

正如 openpub 文档中解释的那样,

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

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

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

当您想要等待线程终止然后在主线程中进一步处理时,这很有用。

解决方案 2:

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

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

  • pthread_exit

  • 线程连接

解决方案 3:

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

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

该方法以受控方式pthread_exit终止您的函数和线程。其特殊性是,以其他方式结束将终止包括所有其他线程在内的整个进程。main`main`main

为了使此方法有效,您必须确保您的所有线程均未使用在其main函数内声明的局部变量。该方法的优点是您main不必知道进程中已启动的所有线程,例如,因为其他线程自己创建了新线程,而这些线程main对此一无所知。

解决方案 4:

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:

您不需要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!
", 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
", (long) t);
     rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
     if (rc) { fprintf(stderr, "failed to create thread #%ld - %s
",
                                (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
", (long) t);
      rc = pthread_join(&threads[t], NULL);
      if (rc) { fprintf(stderr, "failed to join thread #%ld - %s
",
                                (long)t, strerror(rc));
               exit(EXIT_FAILURE);
      }
   }
}

解决方案 6:

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

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

在您的代码中,如果您在 之前将 sleep(或延迟) 放在PrintHello函数中pthread_exit(),则主线程可能会退出并终止整个过程,尽管您的PrintHello函数尚未完成,但它将终止。如果您在从 mainpthrade_join() 调用之前在 main 中使用函数,pthread_exit()它将阻塞主线程并等待完成您的调用线程 ( PrintHello)。

解决方案 7:

唔。

POSIX pthread_exit来自http://pubs.opengroup.org/onlinepubs/009604599/functions/pthread_exit.html的描述:

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() 线程变量保持可访问的想法相反。

解决方案 8:

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

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

另一点是,当其他线程正在运行时,保持主线程处于停止状态可能会在各种情况下引起依赖于实现的问题,例如在主线程中分配资源或者在其他线程中使用主线程本地的变量。

此外,所有共享资源仅在进程退出时才会释放,这不会节省任何资源。所以,我认为应该避免使用pthread_exitin place 。pthread_join

解决方案 9:

调用 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 内存部分中至关重要。

解决方案 10:

  #include<stdio.h>
  #include<pthread.h>
  #include<semaphore.h>
 
   sem_t st;
   void *fun_t(void *arg);
   void *fun_t(void *arg)
   {
       printf("Linux
");
       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
");
       //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 可以工作,您可以亲自查看。

确保您理解一旦创建了线程,该进程将与主进程同时运行。

  1. 如果没有 join,主线程继续运行并返回 0

  2. 使用连接后,主线程将卡在 while 循环中,因为它等待线程执行完成。

  3. 使用 join 并删除注释掉的 pthread_exit,线程将在运行 while 循环之前终止,并且 main 将继续

  4. pthread_exit 的实际用法可以用作 if 条件或 case 语句,以确保某些代码的 1 个版本在退出之前运行。

void *fun_t(void *arg)
   {
       printf("Linux
");
       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
");
    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
");
    pthread_join(pt,NULL);
    printf("After_thread
");
    
}

注意到 fun_t 是在“before thread”之后运行的。如果从上到下呈线性,则预期输出将是 before thread、I'm thread、after thread。但在这种情况下,我们会阻止主程序进一步运行,直到 func_t 释放信号量。结果可以通过https://www.onlinegdb.com/进行验证

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用