linux-线程

本文介绍了Linux线程的概念,包括线程与Windows线程的区别、线程间的共享与非共享资源、线程与进程的对比。详细讲解了线程控制函数,如pthread_self、pthread_create、pthread_exit、pthread_join、pthread_detach和pthread_cancel,并给出了线程使用时的注意事项,如线程库版本一致性、线程回收和避免使用信号等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

linux-线程

在这里插入图片描述

- 线程的概念

1>linux的线程与windos的线程区别很大(win的线程比较完善,进程不行,linux的进程发展的更早)

2>每一个进程都会分配0-4G的虚拟空间地址,而线程共享0-4G的虚拟地址空间,但每一个线程有自己不同的pcb.但pcb中指向内存资源的三级页表是相同的
在这里插入图片描述三级页表(mmu映射虚拟地址到物理内存的过程)
在这里插入图片描述

3>线程是最小的执行单位(抢占cpu的最小单位(根据线程的LWP号(使用ps -Lf pid//查看指定的线程LWP号))),进程是最小的资源分配单位(0-4G资源分配).
4>进程可以蜕变成线程
5>线程可看做是寄存器和栈的集合

- 线程之间共享的资源和非共享的资源

1>线程共享的资源
a:文件描述符表
b:每种信号的处理方式(所以线程不建议使用信号,共享处理,互相干扰)
c:当前工作目录(因为创建线程需要进程,进程不变,多个线程自然共享工作目录)
d:用户ID和用户组ID
e:内存地址空间(.text/.data/.bss/heap/共享库)(除开了栈空间:因为栈空间存储的每个线程的函数h和变量)
2>线程独享的资源
a:线程ID
b:处理器现场和栈指针(内核栈)
c:独立的栈空间(用户空间栈)
d:errno变量
e:信号屏蔽字(系统阻塞信号集)
f:调度优先级

- 线程与进程的对比

优点:1>程序的并发性更高(抢占cpu的执行时间更强);2>开销小(与进程相差不是特别大);3>数据通信,共享数据方便;(因为工共享用户区(出去栈空间))
缺点(都不是很突出的问题):1>线程多使用库函数,相比系统函数不稳定;2>不支持gdb调试;3>对信号支持不友好;

- 线程控制函数

1>pthread_self()函数:相当于getpid();获取当前线程的ID号(用于进程间识别不同的线程).(注意:不应使用pthread_create()函数的传出参数获取线程的ID号,而应使用该函数)
2>pthread_create()函数(相当于fork()函数)
注意:linux环境下,所有线程特点,失败若有返回值,直接返回的是编号;(区别进程)
3>pthread_exit()函数(注意:exit();pthread_exit();return ;三者在线程编程中的使用)
a:pthread_exit:结束调用该函数的线程;
b:return:返回到调用函数的地方(上一级)
c:exit:结束进程,所有的线程也会结束(线程编程慎重使用)
4>pthread_join()函数(相当于wait()函数(阻塞等待回收))
注意:进程是父进程给子进程回收,但是线程可以不,子线程也可以调用该函数进行回收指定的线程.
5>pthread_detach()函数(线程独有的)
将线程分离,分离后的线程结束后自动清理pcb控制部分的资源回收;常用于服务器里.分离后的线程就不需要pthread_join()函数回收(回收分离线程会报错);
6>pthread_cancel()函数
注意:杀死(取消)线程,相当于进程的kill()函数,但是pthread_cancel()函数不是实时的,必须到达取消点(比如系统调用:pause,wait,等函数,man 7 pthreads查看取消点)才会取消.

#include<stdio.h>
#include <pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include <string.h>

	/*******************************************************
	*函数:pthread_self() 
	*头文件: #include <pthread.h>
	*格式: pthread_t pthread_self(void); //pthread_t:typedef unsigned long int类型
    *注意: Compile and link with -pthread(编译和链接时加上-pthread参数)
    *作用:获取当前进程的ID号
	*返回值:returning the calling thread's ID.
	********************************************************/
	
	/*******************************************************
	*函数:pthread_create() 
	*头文件: #include <pthread.h>
	*格式:int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
	*参数:pthread_t *thread:传出参数;保存系统分配的线程号;
	*const pthread_attr_t *attr:通常传入NULL,表示使用线程的默认属性.(可以通过此参数将创建的线程设置为游离状态(分离),线程栈空间大小的修改(容纳更多的线程))
	*void *(*start_routine) (void *):函数指针(函数返回值为 void *,参数为 void *类型的),指向表示线程体的函数,该函数运行完,
	*新创建的线程就结束.void *arg:线程主体函数的参数;
    	*注意: Compile and link with -pthread(编译和链接时加上-pthread参数)
   	 *作用:创建一个新的线程,主线程继续向下执行,而子线程去执行start_routine函数的动作
	*返回值:On  success,  pthread_create() returns 0; on error, it returns an error number, and the contents of *thread are undefined.
	********************************************************/
		/*******************************************************
	*函数:pthread_detach() 
	*头文件: #include <pthread.h>
	*格式:int pthread_detach(pthread_t thread);//pthread_t thread: pthread_create()第一个参数返回的线程号
    *注意: Compile and link with -pthread(编译和链接时加上-pthread参数)
   	*作用:分离线程,线程结束后自动回收内核资源,不需要再次调用pthread_join()进行回收.
	*返回值:On  success,  pthread_detach() returns 0; on error, it returns an error number.
	********************************************************/
	/*******************************************************
	*函数:pthread_join() 
	*头文件: #include <pthread.h>
	*格式:int pthread_join(pthread_t thread, void **retval);//pthread_t thread:pthread_create()函数创建线程时第一个参数传出的ID值;
	*void **retval:接受线程的返回值.(注意类型转换)
    *注意: Compile and link with -pthread(编译和链接时加上-pthread参数)
   	*作用:阻塞回收指定子线程(相当于进程中的wait()函数)(在回收的时候可以传递参数出来)
	*返回值:On  success,  pthread_join()  returns  0; on error, it returns an error number.
	********************************************************/
	/*******************************************************
	*函数:pthread_exit() 
	*头文件: #include <pthread.h>
	*格式:  void pthread_exit(void *retval);//void *retval:范型参数,无返回值
    *注意: Compile and link with -pthread(编译和链接时加上-pthread参数)
    *作用:terminate calling thread(将调用该函数的线程结束)
	*返回值:无
	*区别: return:返回到调用函数的地方
	*		pthread_exit():将调用该函数的线程结束
	*		exit():结束进程,所有的线程也会结束(线程编程慎用)
	********************************************************/
	/*******************************************************
	*函数:pthread_cancel() 
	*头文件: #include <pthread.h>
	*格式:int pthread_cancel(pthread_t thread);//pthread_t thread:pthread_create()函数创建线程时第一个参数传出的ID值;
    *注意: Compile and link with -pthread(编译和链接时加上-pthread参数)
   	*作用:杀死线程(相当于进程中的kill()函数)
	*返回值:On  success, pthread_cancel() returns 0; on error, it returns a nonzero error number.
	********************************************************/

int var=100;//区分线程,子线程进行更改,全局也会更改,证明线程的全局变量是共享的(进程是:读时共享,写时复制)
int i=1;
int k=1;
//用于pthread_join()函数回收使用
typedef struct exit_c{
	int sta;
	char c;
	int num;
}exit_t;
//子线主体函数
void * pthread_func( void *arg)
{	
	exit_t *retval=(exit_t*)arg;//再一次格式转换	
	sleep(retval->num);
	var=1000;
	retval->c='M';
	retval->sta=retval->num+1;
	printf("%dth thread ID is:%lu,process ID id %d,var=%d\n",retval->num+1,pthread_self(),getpid(),var);
	pthread_exit((void *)retval);//这里等同return 0;但是使用exit(0)会导致进程退出,所有线程也会退出.//注意(void *)retval类型转换;
}

/*******************************************************
*验证线程控制原语
*******************************************************/
int main()
{
	pthread_t tid[5]; //存放五个线程的tid
	int ret,ret1;
	exit_t *retval[5];
	//循环创建5个线程,区别进程(线程不用考虑子线程创建子线程的问题,如果要子线程创建子线程,需要在pthread_func()函数再次调用pthread_create()函数.)
	for(int i=0;i<5;i++)
	{	
		retval[i]=(exit_t*)malloc(sizeof(exit_t));
		retval[i]->num=i;
		ret=pthread_create(&tid[i],NULL,pthread_func,(void *)retval[i]);//注意:(void *)retval:retval是需要转换类型的,进行的是传值操作;如果这里传入retval,进行传地址操作;
        //会导致错误(5个线程的产生很快完成,但是线程的主体函数不一定运行完,所以就会导致上一个线程与下一个线程的值相等,所以在传地址的时候保证地址的唯一性;(创建一个线程就均可传值\传地址)
        //ret1=pthread_detach(tid[i]);//如果此处设置线程分离,后面就不用调用pthread_join()函数进行线程回收                                      	             	 	
		                                                   		 	 
		if(ret!=0)
		{
			 fprintf(stderr,"pthread_create error:%s\n",strerror(ret));  //char *strerror(int errnum);//该函数只需传入错误号,然后就会输出对应的错误描述(进程编程就会直接返回错误描述);int fprintf(FILE *stream, const char *format, ...);
			 exit(1);
		}
	}
	sleep(i);//保证每个线程都去运行线程主体函数

	//回收5个线程
	
	sleep(6);//保证子线程创建完毕
	for(int i=0;i<5;i++)
	{	
		
		pthread_join(tid[i],(void **)&retval[i]);//阻塞等待回收子线程;//(void **)&retval[i]一级指针转为二级指针:先取地址,再转为二级指针
		printf("**************************************************\n");
		printf("%dth thread was Recycled: sta:%d; c:%c; \n", retval[i]->num+1 ,retval[i]->sta,retval[i]->c);
		free(retval[i]);
	}
	printf("*******in mian thread ID is:%lu,process ID id %d,var=%d*********\n",pthread_self(),getpid(),var);
	
	pthread_exit(NULL);//主控线程结束,并不会导致进程ID号的改变// 这里如果不加slepp(); return 0;和exit(0);都会是进程退出;
}

运行结果:
在这里插入图片描述

- 线程\进程控制函数对比

在这里插入图片描述注意:分离线程回收会报错22(所以分离的线程不用回收),杀死的线程回收值为-1;(pthread_testcancel()函数是加入取消点的作用)

- 线程使用的注意事项

1>注意NPTL库版本一致(使用命令查看:getconf GNU_LIBPTHREAD_VERSION)(GNU提供线程库,编程平台不一样时需要保证该库的版本一致)
2>编译的时候加上 -pthread (引入线程库)
3>主线程退出,其他线程不退出时应该调用pthread_exit(),如果用其他的(return exit)导致进程退出,全部线程也会退出
4>避免僵尸线程:1:pthread_join()回收线程;2:pthread_detach()函数分离线程;3:pthread_creat()函数第二个参数指定创建的线程为游离态;
5>各个线程共享堆区,所以malloc()和mmap分配的空间可以区其他线程释放;
6>避免在线程里使用fork()函数
7>避免在线程里引入信号机制;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值