【Linux C | 多线程编程】线程的连接、分离,资源销毁情况

这篇具有很好参考价值的文章主要介绍了【Linux C | 多线程编程】线程的连接、分离,资源销毁情况。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
⏰发布时间⏰:2024-04-01 14:52:46

本文未经允许,不得转发!!!


【Linux C | 多线程编程】线程的连接、分离,资源销毁情况,Linux系统编程🏆,linux,c语言,线程的连接,pthread_join,线程的分离,pthread_detach,线程退出后资源情况

🎄一、概述

记住一句话,“创建线程后,要么连接该线程,要么使该线程分离,否则可能导致资源无法释放”。

怎样连接一个线程,连接线程是注意什么?
为什么要连接线程?
怎样分离一个线程,分离线程是注意什么?

本文将围绕线程的连接、分离操作去召开,让读者可以清楚上面几个问题的答案。


【Linux C | 多线程编程】线程的连接、分离,资源销毁情况,Linux系统编程🏆,linux,c语言,线程的连接,pthread_join,线程的分离,pthread_detach,线程退出后资源情况

🎄二、线程的连接 pthread_join

线程连接(joining):使用 pthread_join 函数,用来等待某线程的退出并接收它的返回值。

线程连接可以用来等待一个线程执行,也可以用来获取线程的返回值,也可以即等待线程的结束又获取线程的返回值。这个等待有点类似于进程等待子进程退出的wait操作,但是有两点区别:

  • 1、进程间的等待只能是父进程等待子进程,而线程则没有这样的说法,只要是在一个线程组(进程)内,就可以对另外一个线程执行连接(join)操作;
  • 2、进程可以等待任一子进程的退出。而线程没有这样的操作,需要明确指定要连接的线程ID。这样的设计可以避免库函数的线程被连接了而导致库函数无法连接自己的线程。

pthread_join函数原型:

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
Compile and link with -pthread.
  • 函数描述:pthread_join可以等待某线程的退出并接收它的返回值。根据等待的线程是否退出, 可得到如下两种情况:
    • 等待的线程尚未退出, 那么pthread_join的调用线程就会陷入阻塞。
    • 等待的线程已经退出, 那么pthread_join函数会将线程的退出值(void*类型) 存放到retval指针指向的位置。
  • 函数参数:
    • thread:传入参数,要连接的线程ID;
    • retval:传出参数,用来接收线程返回值。
  • 函数返回值:成功返回 0,调用失败,和pthread_create函数一样, errno作为返回值返回。
    • EDEADLK:死锁,如自己连接自己,或者A连接B,B又连接A;
    • EINVAL:线程不是一个可连接(joinable)的线程;
    • EINVAL:已经有其他线程捷足先登,连接目标线程;
    • ESRCH:传入的线程ID不存在,查无此线程。

可能产生连接死锁的两个情况:
1、线程A连接线程A(自己连接自己);
2、线程A连接线程B,线程B连接线程A。
【Linux C | 多线程编程】线程的连接、分离,资源销毁情况,Linux系统编程🏆,linux,c语言,线程的连接,pthread_join,线程的分离,pthread_detach,线程退出后资源情况

🌰举例子:

// 07_pthread_join.c
// gcc 07_pthread_join.c -l pthread
#include <stdio.h>
#include <pthread.h>

int ret = -1;		// 全局变量记录线程返回值
void *func(void *arg)
{
	int *parg = arg;
	printf("this thread arg is %d, my threadID is %lx \n", *parg, (unsigned long)pthread_self());
	ret=*parg+1;
	return (void*)&ret;
}
int main()
{
	int arg=10;
	pthread_t threadId;
	pthread_create(&threadId, NULL, func, &arg);
	int *pRet = NULL;
	pthread_join(threadId, (void**)&pRet);
	printf("pthread_join end, ret=%d\n", *pRet);
	return 0;
}

【Linux C | 多线程编程】线程的连接、分离,资源销毁情况,Linux系统编程🏆,linux,c语言,线程的连接,pthread_join,线程的分离,pthread_detach,线程退出后资源情况

🎄三、线程的分离 pthread_detach

默认情况下, 新创建的线程处于可连接(Joinable)的状态, 可连接状态的线程退出后, 需要对其执行连接操作, 否则线程资源无法释放,从而造成资源泄漏。

有时,我们并不关心该线程的返回值,也不想阻塞等待,但不执行连接又会资源泄露,那怎么办?线程库考虑到这种使用场景,提供了 pthread_detach 函数可将线程设置成已分离(detached)状态。处于已分离(detached)状态的线程退出时,由系统负责回收该线程的资源。

pthread_detach函数原型:

#include <pthread.h>
int pthread_detach(pthread_t thread);
Compile and link with -pthread.
  • 函数描述:pthread_detach函数将thread指定的线程标记为已分离。当一个分离的线程终止时,它的资源会自动释放回系统,而不需要另一个线程与终止的线程连接。试图分离已分离的线程会导致未指定的行为。
  • 函数参数:thread,要分离的线程ID。
  • 函数返回值:成功返回 0,调用失败,和pthread_create函数一样, errno作为返回值返回。
    • EINVAL:线程不是一个可连接(joinable)的线程,已经处于已分离状态;
    • ESRCH:传入的线程ID不存在,查无此线程。

其他相关内容:

  • 1、将线程的属性设定为已分离的第2种方式,使用 pthread_attr_setdetachstate 函数,这个比较少用可以了解一下。
    #include <pthread.h>
    int pthread_attr_setdetachstate(pthread_attr_t *attr,int detachstate);
    int pthread_attr_getdetachstate(pthread_attr_t *attr,int *detachstate);
    
  • 2、线程分离可由其他线程对其执行分离,也可以线程自己执行pthread_detach函数, 将自身设置成已分离的状态:
    pthread_detach(pthread_self());
    
  • 3、所谓已分离, 并不是指线程失去控制, 不归线程组管理, 而是指线程退出后, 系统会自动释放线程资源。

🌰举例子:下面例子是主线程执行分离的,更常见的是,线程内部自己执行分离。

// 07_pthread_detach.c
// gcc 07_pthread_detach.c -l pthread
#include <stdio.h>
#include <pthread.h>
void *func(void *arg)
{
	int *parg = arg;
	printf("this thread arg is %d, my threadID is %lx \n", *parg, (unsigned long)pthread_self());
	return NULL;
}
int main()
{
	int arg=10;
	pthread_t threadId;
	pthread_create(&threadId, NULL, func, &arg);
	pthread_detach(threadId);	// 对该线程执行分离
	printf("pthread_detach exec\n");
	while(1); // 保持主线程不退出
	return 0;
}

【Linux C | 多线程编程】线程的连接、分离,资源销毁情况,Linux系统编程🏆,linux,c语言,线程的连接,pthread_join,线程的分离,pthread_detach,线程退出后资源情况

🎄四、必须连接线程 或 分离线程

这一小节,我们了解为什么必须要 连接线程分离线程 ?
因为既不分离线程又不连接已经退出的线程,可能会导致资源无法释放。

注意已连接已分离 的线程退出时,该线程的资源并没有立即调用munmap来释放掉,而是保留着被后面新建的线程复用。NPTL线程库的设计。
释放线程资源的时候,NPTL认为进程可能再次创建线程,而频繁地munmap和mmap会影响性能,所以NTPL将该栈缓存起来,放到一个链表之中,如果有新的创建线程的请求,NPTL会首先在栈缓存链表中寻找空间合适的栈,有的话,直接将该栈分配给新创建的线程。

下面通过一个例子来看看线程退出后的资源情况。

// 07_pthread_join_detach_test.c
// gcc 07_pthread_join_detach_test.c -l pthread -DNO_JOIN_DETACH	// 没有连接、分离
// gcc 07_pthread_join_detach_test.c -l pthread -DTHREAD_JOIN		// 使用线程的连接
// gcc 07_pthread_join_detach_test.c -l pthread -DTHREAD_DETACH		// 使用线程的分离
#include <stdio.h>
#include <pthread.h>
#include <sys/syscall.h>
void *func(void *arg)
{
#ifdef THREAD_DETACH
	pthread_detach(pthread_self());
#endif
	printf("threadID = %lx, TID=%u \n", (unsigned long)pthread_self(), syscall(SYS_gettid));
	
	// 获取线程属性
	pthread_attr_t gattr;
	int s = pthread_getattr_np(pthread_self(), &gattr);
	if (s != 0)
	{
		printf("pthread_getattr_np error\n");
		return NULL;
	}
	
	// 获取线程栈地址和大小
	void *stkaddr;
	size_t v;
	s = pthread_attr_getstack(&gattr, &stkaddr, &v);
	if (s != 0)
	{
		printf("pthread_attr_getstackaddr error\n");
		return NULL;
	}
	printf("Stack address = %p, size=%luk btye\n", stkaddr, v/1024);
	sleep(3);
	printf("TID=%u EXIT\n", syscall(SYS_gettid));
	return NULL;
}
int main()
{
#ifdef NO_JOIN_DETACH
	printf("NO_JOIN_DETACH PID=%u\n",syscall(SYS_gettid));
#endif
#ifdef THREAD_JOIN
	printf("THREAD_JOIN PID=%u\n",syscall(SYS_gettid));
#endif
#ifdef THREAD_DETACH
	printf("THREAD_DETACH PID=%u\n",syscall(SYS_gettid));
#endif

	// 1、创建第一个线程
	pthread_t threadId, threadId2;
	pthread_create(&threadId, NULL, func, NULL);

	// 2、等待上个线程结束
#ifdef THREAD_JOIN
	pthread_join(threadId, NULL);
#else // 没有等待线程,就使用sleep等待上个线程结束
	sleep(5);
#endif
	
	// 3、创建第二个线程
	pthread_create(&threadId2, NULL, func, NULL);

	pause(); // 保持主线程不退出
	return 0;
}

下面代码演示了线程资源使用的三个情况,下面分别看看其运行结果:

  • 🌰1、没有连接、分离;
    复制上面代码,运行gcc 07_pthread_join_detach_test.c -l pthread -DNO_JOIN_DETACH编译,可以看到各个线程的栈地址不一样:
    【Linux C | 多线程编程】线程的连接、分离,资源销毁情况,Linux系统编程🏆,linux,c语言,线程的连接,pthread_join,线程的分离,pthread_detach,线程退出后资源情况
    运行pmap查看内存分布情况,也可以看到这两个地址,如下图:
    【Linux C | 多线程编程】线程的连接、分离,资源销毁情况,Linux系统编程🏆,linux,c语言,线程的连接,pthread_join,线程的分离,pthread_detach,线程退出后资源情况
    可以得出一个结论:如果线程既不连接、又不分离的话,那么:
    1) 已经退出的线程,其空间没有被释放,仍然在进程的地址空间之内。
    2) 新创建的线程,没有复用刚才退出的线程的地址空间。

  • 🌰2、使用线程的连接;
    接下来看看,使用了线程连接的情况,上面代码运行gcc 07_pthread_join_detach_test.c -l pthread -DTHREAD_JOIN编译,运行结果如下,可以看到两个线程的栈地址是一样的,也就是说,第一个线程退出后,其地址空间被后面新建的线程复用
    【Linux C | 多线程编程】线程的连接、分离,资源销毁情况,Linux系统编程🏆,linux,c语言,线程的连接,pthread_join,线程的分离,pthread_detach,线程退出后资源情况
    运行pmap查看进程内存分布情况,只有一个线程栈地址,如下图:
    【Linux C | 多线程编程】线程的连接、分离,资源销毁情况,Linux系统编程🏆,linux,c语言,线程的连接,pthread_join,线程的分离,pthread_detach,线程退出后资源情况

  • 🌰3、使用线程的分离。
    接下来看看,使用了线程分离的情况,上面代码运行gcc 07_pthread_join_detach_test.c -l pthread -DTHREAD_DETACH编译,运行结果如下,可以看到两个线程的栈地址是一样的,也就是说,第一个线程退出后,其地址空间被后面新建的线程复用
    【Linux C | 多线程编程】线程的连接、分离,资源销毁情况,Linux系统编程🏆,linux,c语言,线程的连接,pthread_join,线程的分离,pthread_detach,线程退出后资源情况
    运行pmap查看进程内存分布情况,只有一个线程栈地址,如下图:
    【Linux C | 多线程编程】线程的连接、分离,资源销毁情况,Linux系统编程🏆,linux,c语言,线程的连接,pthread_join,线程的分离,pthread_detach,线程退出后资源情况


【Linux C | 多线程编程】线程的连接、分离,资源销毁情况,Linux系统编程🏆,linux,c语言,线程的连接,pthread_join,线程的分离,pthread_detach,线程退出后资源情况

🎄五、总结

本文结束了Linux系统编程的线程的连接(pthread_join)、线程的分离(pthread_detach),以及介绍了为什么要使用线程的连接、分离。

【Linux C | 多线程编程】线程的连接、分离,资源销毁情况,Linux系统编程🏆,linux,c语言,线程的连接,pthread_join,线程的分离,pthread_detach,线程退出后资源情况
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

参考资料:
《Linux环境编程:从应用到内核》文章来源地址https://www.toymoban.com/news/detail-849478.html

到了这里,关于【Linux C | 多线程编程】线程的连接、分离,资源销毁情况的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • linux系统编程(7)--线程

    在许多经典的操作系统教科书中,总是把进程定义为程序的执行实例,它并不执行什么, 只是维护应用程序所需的各种资源,而线程则是真正的执行实体。 所以,线程是轻量级的进程(LWP:light weight process),在Linux环境下线程的本质仍是进程。为了让进程完成一定的工作,进

    2023年04月08日
    浏览(23)
  • Linux系统编程:线程控制

    目录 一. 线程的创建 1.1 pthread_create函数 1.2 线程id的本质 二. 多线程中的异常和程序替换 2.1 多线程程序异常 2.2 多线程中的程序替换 三. 线程等待 四. 线程的终止和分离 4.1 线程函数return 4.2 线程取消 pthread_cancel 4.3 线程退出 pthread_exit 4.4 线程分离 pthread_detach  五. 总结

    2024年02月11日
    浏览(31)
  • Linux系统编程5(线程概念详解)

    线程同进程一样都是OS中非常重要的部分,线程的应用场景非常的广泛,试想我们使用的视频软件,在网络不是很好的情况下,通常会采取下载的方式,现在你很想立即观看,又想下载,于是你点击了下载并且在线观看。学过进程的你会不会想,视频软件运行后在OS内形成一个

    2024年02月10日
    浏览(30)
  • 5.3 连接和分离线程

    pthread_join(thread, status) pthread_detach(thread) pthread_attr_setdetachstate(attr, detachstate) pthread_attr_getdetachstate(attr)

    2024年02月05日
    浏览(27)
  • 【Linux系统无法连接网络,修改IP地址和网关,ping解决主机不可达的情况】

    Ubuntu出现无法连接网络,ping公网IP地址显示主机不可达,打开FireFox浏览器打不开网页的情况,还有在使用sudo apt-get install gcc 下载某个工具的时候出现无法解析当前域名的情况,其实都是归终于网络无法连接,我尝试过很多办法,在教程上面看到的修改Network Manager.state文件的

    2024年02月08日
    浏览(44)
  • 《Linux操作系统编程》 第十章 线程与线程控制: 线程的创建、终止和取消,detach以及线程属性

    🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍》学会IDEA常用操作,工作效率翻倍~💐 🪁🍁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬

    2024年02月11日
    浏览(80)
  • JUC并发编程-集合不安全情况以及Callable线程创建方式

    1)List 不安全 ArrayList 在并发情况下是不安全的 解决方案 : 1.Vector 2.Collections.synchonizedList() 3. CopyOnWriteArrayList 核心思想 是,如果有 多个调用者(Callers)同时要求相同的资源 (如内存或者是磁盘上的数据存储),他们 会共同获取相同的指针指向相同的资源 , 直到某个调用者

    2024年01月23日
    浏览(38)
  • 【Linux】线程分离 | 线程库 | C++调用线程 | 线程局部存储

    1. 为什么要线程分离? 使用 pthread_join 默认是阻塞的 ,即主线程等待 新线程退出 在这个过程中,主线程会直接卡住,就没办法继续向后运行,也就什么都干不了 若主线程 想做其他事情 ,所以就提出了 线程分离的概念 默认情况下,新创建的线程是joinable的 即 线程默认被创

    2024年02月06日
    浏览(28)
  • Vue.js 组件销毁有几种情况?

    Vue.js 组件销毁有以下几种情况: 显式销毁:在组件实例上调用 $destroy() 方法可以显式销毁一个组件实例,它会解除所有的绑定并移除 DOM 中的元素。一般来说,你不需要显式地销毁组件实例,因为 Vue.js 会自动管理它们。 条件渲染:当一个组件通过 v-if 或 v-show 指令被移除时

    2024年02月12日
    浏览(37)
  • 查看centos7系统资源使用情况

    目录 1、df查看系统硬盘使用情况 2、free查看系统内存使用情况 3、vmstat查看系统内存、IO、CUP系统情况 4、top查看系统资源情况 5、uptime查看时间与负载 6、ps查看系统进程状态 1、df查看系统硬盘使用情况 2、free查看系统内存使用情况 3、vmstat查看系统内存、IO、CUP系统情况 主要

    2024年02月10日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包