10.4 Linux_并发_线程

news/2024/10/3 19:00:44 标签: linux

概述

线程的共享资源:

可执行的指令、静态数据、文件描述符、当前工作目录、用户ID、用户组ID

线程的私有资源:

线程ID、程序计数器PC和相关寄存器、堆栈、错误号、优先级、执行状态和属性

线程编译:

gcc <.c文件> -l pthread -o <可执行文件>

线程的运行特点:

如果主线程main退出,则其他线程也被终止。因为main退出,进程的空间被释放,所有线程的空间也不复存在。

命令行查看线程状态:

ps -eLf | grep <程序名>

相关函数

1、创建线程

函数声明如下:

int pthread_create(pthread_t *thread, 
                   const pthread_attr_t *attr,
                   void *(*start_routine) (void *), 
                   void *arg);

返回值:成功返回0,失败返回错误码

thread:线程对象,将创建的新线程的TID号存入该变量

attr:线程的属性,NULL代表默认的属性。

         对于默认属性,线程占有的资源不会因执行结束而释放。

start_routine:线程的回调函数

arg:线程的回调函数的参数

2、线程退出

函数声明如下:

void pthread_exit(void *retval);

retval:线程退出时的返回值,该值可被pthread_join获取。

注意:当线程调用该函数后,线程结束,但线程的资源未被回收,最终资源被pthread_join回收。即:功能与exit退出进程类似

3、获取自己的线程ID(TID)

函数声明如下:

pthread_t pthread_self(void);

返回值:自己的TID号

4、线程回收

函数声明如下:

int pthread_join(pthread_t thread, void **retval);

返回值:成功返回0,失败返回错误码

thread:线程ID

retval:接收pthread_exit的返回值

注意:调用该函数是线程会进入阻塞状态,直到被等待的线程调用pthread_exit结束。被等待的线程结束后,pthread_join会回收退出线程的资源,并且解除阻塞

注意:pthread_join只能依次回收线程。比如先回收线程0,再回收线程1,如果这时线程0一直不结束,而线程1先结束,此时线程1不会被回收,而是等待线程0结束之后,线程1才回收。

5、线程分离

5.1 调用函数 

函数声明如下:

int pthread_detach(pthread_t thread);

返回值:成功返回0,失败返回错误码 

thread:线程ID

注意:调用该函数后指定的线程与主控线程断开关系,指定的线程结束后不会产生僵尸线程,即:自动回收资源。该函数可以由主线程调用,也可以由新创建的线程调用。

5.2 创建时设置线程属性 

函数声明如下:

//初始化线程属性变量attr
int pthread_attr_init(pthread_attr_t *attr);
//设置线程属性
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

attr: 线程属性

detachstate:分离状态,写 PTHREAD_CREATE_DETACHED

设置完成之后,将attr传入pthread_create的第二个参数attr即可在创建时完成线程分离。

6、取消线程

取消线程的作用是让主线程能够主动的让创建的线程进行退出,也可以让自己取消自己。

函数声明如下:

//取消线程
int pthread_cancel(pthread_t thread);
//手动设置取消点
void pthread_testcancel(void);
//设置取消状态
int pthread_setcancelstate(int state, int *oldstate);
//设置取消的时刻
int pthread_setcanceltype(int type, int *oldtype);

thread:线程ID

state:线程是否可以被取消

            允许被取消:PTHREAD_CANCEL_ENABLE(默认)

            禁止被取消:PTHREAD_CANCEL_DISABLE

type:线程取消时刻的模式

           等到取消点才取消:PTHREAD_CANCEL_DEFERRED(默认)

           目标线程会立刻取消:PTHREAD_CANCEL_ASYNCHRONOUS

oldstate:传入NULL

注意:线程的取消要有取消点,取消点就是阻塞的系统调用。当没有取消点时,该函数不会进入阻塞,并且对指定的线程也没有任何影响。

注意:调用pthread_cancel,指定的线程被取消,这意味着该线程在调用pthread_exit之前进行了退出。除此之外,pthread_cancel会自动回收线程的资源。

pthread_testcancel的使用:

该函数手动设置取消点,并不是说执行到该函数才设置,而是线程中存在这个函数,不论能否执行到,都可以进行取消。???????

pthread_setcancelstate的使用:

pthread_setcancelstate用于保护线程中的部分代码段完整执行,在这期间线程不可被取消。

使用方法如下:

pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);

//线程中不可被取消的代码段

pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);

示例见示例代码:设置线程是否可以被取消

注意:在创建完线程之后,如果直接取消该线程,那么该线程中尽管在刚开始时就禁止取消,也有可能被取消。因为创建线程需要时间,当主线程取消线程时,新的线程还没有执行禁止取消的函数。

7、线程清理

线程清理的作用是释放线程取消之前申请的内存空间,这个释放代码需要自己编写。

函数声明如下:

void pthread_cleanup_push(void (*routine)(void *),void *arg);

void pthread_cleanup_pop(int execute);

routine:清理函数

arg:清理函数的参数

execute:写0代表不执行routine,非0代表执行routine

注意:这两个函数必须成对使用,并且pthread_cleanup_push后不加分号

注意:这两个函数可以多次使用,效果类似栈,先push的后pop

注意:这两个并不是函数,而是宏定义

routine被执行的情况:

  • 当主线程调用pthread_cancel取消了线程
  • 该线程调用了pthread_exit退出了线程
  • 执行到pthread_cleanup_pop,并且传参为非0

注意:当调用return退出线程时,routine不执行

示例代码

1、创建线程

具体代码实现如下:

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

//线程0回调函数
void* pthread_0(void* arg){
	while(1){
		printf("this is pthread 0\n");
		sleep(1);
	}
	//退出线程
	pthread_exit(NULL);
}

//线程1回调函数
void* pthread_1(void* arg){
	while(1){
		printf("this is pthread 1\n");
		sleep(1);
	}
	//退出线程
	pthread_exit(NULL);
}

int main(){

	pthread_t tid[2];//多个线程时,tid用数组存储

	//创建线程
	if(pthread_create(&tid[0],NULL,(void*)pthread_0,NULL) != 0){
		perror("pthread_create");
		return -1;
	}
	if(pthread_create(&tid[1],NULL,(void*)pthread_1,NULL) != 0){
		perror("pthread_create");
		return -1;
	}

	while(1){
		printf("this is main\n");
		sleep(1);
	}
	return 0;
}

代码运行结果如下:

2、获取线程TID

具体代码实现如下:

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

//线程回调函数
void* pthread_0(void* arg){
	//线程中查看自己的TID
	printf("pthread:tid0 = %ld\n",pthread_self());
	//退出线程
	pthread_exit(NULL);
}

int main(){

	pthread_t tid;
	//创建线程
	if(pthread_create(&tid,NULL,(void*)pthread_0,NULL) != 0){
		perror("pthread_create");
		return -1;
	}
	//main中查看所创建线程的TID
	printf("main:tid0 = %ld\n",tid);
	while(1);
	return 0;
}

代码运行结果如下:

3、参数传递

3.1 传递一个参数

传递一个参数,只需要将这个参数的地址进行传递即可。

具体代码实现如下:

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

//线程回调函数
void* pthread_0(void* arg){
	//获取传入的参数
	printf("arg = %d\n",*((int*)arg));
	//退出线程
	pthread_exit(NULL);
}

int main(){

	pthread_t tid;
	int arg0 = 100;
	//创建线程
	if(pthread_create(&tid,NULL,(void*)pthread_0,&arg0) != 0){
		perror("pthread_create");
		return -1;
	}
	while(1);
	return 0;
}

代码运行结果如下:

3.2 传递多个参数

传递多个参数,就是将多个参数捆为一个结构体,传入的是结构体的指针。

具体代码实现如下:

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

//线程回调函数
struct test{
	int num;
	char a;
};
void* pthread_0(void* arg){
	//获取传入的参数
	printf("arg.num = %d\n",((struct test*)arg)->num);
	printf("arg.a = %c\n",((struct test*)arg)->a);
	//退出线程
	pthread_exit(NULL);
}

int main(){

	pthread_t tid;
	struct test arg0 = {300,'a'};

	//创建线程
	if(pthread_create(&tid,NULL,(void*)pthread_0,&arg0) != 0){
		perror("pthread_create");
		return -1;
	}
	while(1);
	return 0;
}

代码运行结果如下:

3.3 传递的参数经常变化

在3.1、3.2中,传递的参数都是固定的,因此传递指针是可以的。但如果参数会在main中发送改变,那么线程在接收到指针后,访问内存的数据不一定是传入时的数据,即:访问时main已经把数据进行了修改。

针对这个问题,可以将数据直接强制转为void*,进行传递。

具体代码实现如下:

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

//线程0回调函数
void* pthread_0(void* arg){
	printf("num:%d,tid = %ld\n",arg,pthread_self());
	//退出线程
	pthread_exit(NULL);
}

int main(){

	pthread_t tid[10];//多个线程时,tid用数组存储
	int i;
	for(i=0;i<10;i++){
		//创建线程
		if(pthread_create(&tid[0],NULL,(void*)pthread_0,(void*)i) != 0){//将i强转为void*
			perror("pthread_create");
			return -1;
		}
	}
	while(1){
		printf("this is main\n");
		sleep(1);
	}
	return 0;
}

代码运行结果如下:

4、等待线程退出

具体代码实现如下:

#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
void* pthread0(void* arg){
	int i;
	for(i=0;i<3;i++){
		printf("this is pthread0\n");
		sleep(1);
	}
	//线程退出
	pthread_exit("pthread0 exit");
}
int main(){

	pthread_t tid;
	void* ret0 = NULL;
	//创建线程
	if(pthread_create(&tid,NULL,pthread0,NULL) != 0){
		perror("pthread_create");
		return -1;
	}
	//等待线程退出,main进入阻塞
	pthread_join(tid,&ret0);
	printf("ret0:%s\n",(char*)ret0);//打印线程0退出状态
	while(1);

	return 0;
}

代码运行结果如下:

5、线程分离

5.1 调用pthread_detach

#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
void* pthread0(void* arg){
	
	//pthread_detach(pthread_self());//也可以在这里进行线程分离
	printf("this is pthread0\n");
	//线程退出
	pthread_exit("pthread0 exit");
}
int main(){

	pthread_t tid;
	pthread_t tid1;
	void* ret0 = NULL;
	//创建线程
	if(pthread_create(&tid,NULL,pthread0,NULL) != 0){
		perror("pthread_create");
		pthread_detach(tid);//线程分离
		return -1;
	}
	while(1);

	return 0;
}

5.2 设置线程属性为分离

#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
void* pthread0(void* arg){
	printf("this is pthread0\n");
	//线程退出
	pthread_exit("pthread0 exit");
}
int main(){

	pthread_t tid;
	pthread_attr_t attr;
	//初始化线程属性
	pthread_attr_init(&attr);
	//设置线程属性为分离
	pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
	//创建线程,创建时传入attr
	if(pthread_create(&tid,&attr,pthread0,NULL) != 0){
		perror("pthread_create");
		return -1;
	}
	while(1);

	return 0;
}

6、取消线程

6.1 基本使用

具体代码实现如下:

#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
void* pthread0(void* arg){
	while(1){
		sleep(1);//进入阻塞,这里就是一个取消点
        //pthread_testcancel();//也可以将阻塞换为该函数,手动设置一个取消点
	}
	//线程退出
	pthread_exit("pthread0 exit");
}
int main(){

	pthread_t tid;
	void* ret0 = NULL;
	//创建线程
	if(pthread_create(&tid,NULL,pthread0,NULL) != 0){
		perror("pthread_create");
		return -1;
	}
	//取消线程
	pthread_cancel(tid);//取消之后pthread_exit没有运行
	pthread_join(tid,&ret0);//这里的ret0不会收到pthread_exit的返回值
	printf("get\n");
	while(1);

	return 0;
}

代码运行结果如下:

6.2 设置线程是否可以被取消 

具体代码实现如下:

#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
void* pthread0(void* arg){
	int i;
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);//线程禁止被取消
	for(i=0;i<3;i++){
		printf("now cannot cancel\n");
		sleep(1);
	}
	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);//线程可以被取消
	while(1){
		printf("now can cancel\n");
		sleep(1);
	}
	pthread_exit("pthread0 exit");
}
int main(){

	pthread_t tid;
	void* ret0 = NULL;
	//创建线程
	if(pthread_create(&tid,NULL,pthread0,NULL) != 0){
		perror("pthread_create");
		return -1;
	}
	sleep(1);//创建线程后不能立刻取消,需要一些时间让线程执行程序
	pthread_cancel(tid);//取消线程
	printf("get\n");
	pthread_join(tid,&ret0);
	printf("get2\n");
	while(1);

	return 0;
}

代码运行结果如下:

7、线程清理

7.1  routine执行情况1

当主线程调用pthread_cancel取消了线程

#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>

void clean(void* arg){
	//该函数应该执行一些清理任务,但这里为演示所以只进行一下打印
	printf("arg = %s\n",(char*)arg);
}
void* pthread0(void* arg){
	
	pthread_cleanup_push(clean,"2") //这里不能加分号
	while(1){
		sleep(1);//这里阻塞当作一个取消点	
	}
	printf("after while\n");	
	pthread_cleanup_pop(1);
}
int main(){

	pthread_t tid;
	void* ret0 = NULL;
	//创建线程
	if(pthread_create(&tid,NULL,pthread0,NULL) != 0){
		perror("pthread_create");
		return -1;
	}
	pthread_cancel(tid);//取消线程
	pthread_join(tid,&ret0);
	printf("get\n");
	while(1);

	return 0;
}

7.2  routine执行情况2

该线程调用了pthread_exit退出了线程

#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>

void clean(void* arg){
	//该函数应该执行一些清理任务,但这里为演示所以只进行一下打印
	printf("arg = %s\n",(char*)arg);
}
void* pthread0(void* arg){
	
	pthread_cleanup_push(clean,"1") //这里不能加分号
	
	pthread_exit(NULL);//线程退出
	printf("after exit\n");
	pthread_cleanup_pop(1);
}
int main(){

	pthread_t tid;
	void* ret0 = NULL;
	//创建线程
	if(pthread_create(&tid,NULL,pthread0,NULL) != 0){
		perror("pthread_create");
		return -1;
	}
	pthread_join(tid,&ret0);
	printf("get\n");
	while(1);

	return 0;
}

7.3  routine执行情况3

执行到pthread_cleanup_pop,并且传参为非0

#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>

void clean(void* arg){
	//该函数应该执行一些清理任务,但这里为演示所以只进行一下打印
	printf("arg = %s\n",(char*)arg);
}
void* pthread0(void* arg){
	
	pthread_cleanup_push(clean,"1") //这里不能加分号
	
	pthread_cleanup_pop(1);
}
int main(){

	pthread_t tid;
	void* ret0 = NULL;
	//创建线程
	if(pthread_create(&tid,NULL,pthread0,NULL) != 0){
		perror("pthread_create");
		return -1;
	}
	pthread_join(tid,&ret0);
	printf("get\n");
	while(1);

	return 0;
}

7.4 多个清理函数

多次push后,先push的后pop。

具体代码实现如下:

#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>

void clean(void* arg){
	//该函数应该执行一些清理任务,但这里为演示所以只进行一下打印
	printf("arg = %s\n",(char*)arg);
}
void* pthread0(void* arg){
	
	pthread_cleanup_push(clean,"1") //这里不能加分号
	pthread_cleanup_push(clean,"2") //这里不能加分号
	pthread_cleanup_push(clean,"3") //这里不能加分号
	
	pthread_cleanup_pop(1);
	printf("after pop\n");
	while(1){
		printf("now in while\n");
		sleep(1);//阻塞作为取消点 
	}
	printf("after while\n");
	pthread_exit(NULL);//线程退出
	printf("after exit\n");
	pthread_cleanup_pop(1);
	pthread_cleanup_pop(1);
}
int main(){

	pthread_t tid;
	void* ret0 = NULL;
	//创建线程
	if(pthread_create(&tid,NULL,pthread0,NULL) != 0){
		perror("pthread_create");
		return -1;
	}
	pthread_cancel(tid);
	pthread_join(tid,&ret0);
	printf("get\n");
	while(1);

	return 0;
}

代码运行结果如下:


http://www.niftyadmin.cn/n/5688845.html

相关文章

数据结构 ——— 单链表oj题:反转链表

目录 题目要求 手搓一个简易链表 代码实现 题目要求 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表 手搓一个简易链表 代码演示&#xff1a; struct ListNode* n1 (struct ListNode*)malloc(sizeof(struct ListNode)); assert(n1);…

深入理解 Solidity 中的支付与转账:安全高效的资金管理攻略

在 Solidity 中&#xff0c;支付和转账是非常常见的操作&#xff0c;尤其是在涉及资金的合约中&#xff0c;比如拍卖、众筹、托管等。Solidity 提供了几种不同的方式来处理 Ether 转账&#xff0c;包括 transfer、send 和 call&#xff0c;每种方式的安全性、灵活性和复杂度各有…

一个简单的摄像头应用程序1

这个Python脚本实现了一个基于OpenCV的简单摄像头应用,我们在原有的基础上增加了录制视频等功能,用户可以通过该应用进行拍照、录制视频,并查看已拍摄的照片。以下是该脚本的主要功能和一些使用时需要注意的事项: 功能 拍照: 用户可以通过点击界面上的“拍照”按钮或按…

【嵌入式系统】第18章 脉宽调试器(PWM)

目录 18.1 结构框图 18.3 功能说明 18.3.4 PWM 信号发生器 18.3.5 死区发生器 18.3.6 中断/ADC 触发选择器 18.3.7 同步方法 18.3.8 故障条件 18.3.9 输出控制块 LES 硬件介绍&#xff08;12&#xff09;正交编码接口QEI 19.1 结构框图 19.2 信号描述 19.3 功能说明…

长期提供APX515/B原装二手APX525/B音频分析仪

Audio Precision APx515 是一款针对生产测试而优化的高性能音频分析仪。它因其速度、性能、自动化和易用性而成为一流的仪器。它具有卓越的性能&#xff0c;具有 –106 dB 的典型 THDN、1M 点 FFT 和 192k 数字 I/O&#xff0c;以及所有 APx 系列音频分析仪的一键式自动化和易用…

FreeRTOS篇15:中断管理

一.中断优先级 任何中断的优先级都大于任务&#xff01; 在我们的操作系统&#xff0c;中断同样是具有优先级的&#xff0c;并且我们也可以设置它的优先级&#xff0c;但是他的优先 级并不是从 015 &#xff0c;默认情况下它是从 515 &#xff0c;0~4 这 5 个中断优先级不是 F…

AI情感陪伴新纪元:WT2605C语音芯片在成人用品中的创新应用

在探索成人用品领域的无限可能时&#xff0c;科技的每一次进步都为我们带来了前所未有的体验。而今&#xff0c;WT2605C AI语音芯片的引入&#xff0c;正悄然改变着这一传统行业的面貌&#xff0c;为成人用品赋予了全新的情感陪伴功能&#xff0c;开启了智能化、个性化的新时代…

TransFormer 视频笔记

TransFormer BasicsAttention单头注意力 single head attentionQ&#xff1a; query 查寻矩阵 128*12288K key matrix 128*12288SoftMax 归一 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/19e3cf1ea28442eca60d5fc1303921f4.png)Value matrix 12288*12288 MLP Bas…