在 Linux 中何时使用 pthread_exit() 以及何时使用 pthread_join()?
- 2024-11-01 08:41:00
- admin 原创
- 54
问题描述:
我是 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_exit
in 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 可以工作,您可以亲自查看。
确保您理解一旦创建了线程,该进程将与主进程同时运行。
如果没有 join,主线程继续运行并返回 0
使用连接后,主线程将卡在 while 循环中,因为它等待线程执行完成。
使用 join 并删除注释掉的 pthread_exit,线程将在运行 while 循环之前终止,并且 main 将继续
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/进行验证
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件