当前位置: 首页 > article >正文

线程(Thread)

一、概念

线程:线程是一个轻量级的进程

 二、线程的创建

1、线程的空间

(1)进程的空间包括:系统数据段、数据段、文本段

(2) 线程位于进程空间内部

(3) 栈区独享、与进程共享文本段、数据段、堆区

2、线程与进程的关系

进程:进程是操作系统资源分配的最小单元(进程:文本段+数据段+系统数据段)

线程:线程是CPU任务调度的最小单元

        (1)每个线程独立拥有8M(默认)栈空间

        (2)其余的文本段、数据段、堆区都是与进程及进程内的其余线程共享的

三、线程的调度

(1)线程的调度等价于进程的调度(宏观并行,微观串行)

         线程之间宏观并行,微观串行;所以线程在执行的过程中,会出现以下情况:
                情况一:线程1先执行,线程二后执行
                情况二:线程2先执行,线程1后执行
                情况三:线程2执行一半后执行线程1,线程1执行一半后执行线程2,即二者交叉执行

(2)线程是CPU任务调度的最小单元

四、线程的消亡 

(1)与进程消亡保持一致,进程结束时不论线程处于什么状态,线程被强制结束

(2)僵尸线程:等到线程结束需要回收线程空间,否则会产生僵尸线程

五、多进程vs多线程

1、执行效率

多线程 > 多进程

线程 间任务的切换是在同一片进程空间内部完成任务切换,资源开销小

进程 间任务的切换需要映射到不同的物理地址空间,频繁切换资源开销大

2、多任务间通信的实现

多线程 > 多进程

多线程 拥有共享空间(多个线程位于一个进程内,共享该进程的数据区、文本区),通信更加方便

多进程 没有共享空间(进程间是独立的),需要更为复杂的方法实现多进程间通信

3、多任务通信机制的复杂程度

多线程 > 多进程

多线程 操作全局变量会引入资源竞争,需要加锁来解决

多进程 没有资源竞争

4、安全性

多进程 > 多线程

线程 异常崩溃导致进程崩溃,该进程中的其余线程异常结束

进程 异常结束,不会影响其余进程

六、线程相关的函数接口

pthread_create == fork 

pthread_exit == exit 

pthread_join == wait 

 1、pthread_creat

man 3 pthread_create

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

功能:

        在调用函数的进程中创建一个线程

参数:

        thread:存放线程ID空间的首地址

        attr:线程的属性

        start_routine:线程函数的入口

                             void *(*start_routine) (void *)函数指针,返回值类型void*,参数void*,*start_routine:函数首地址(函数名本身就是函数首地址)

        arg:传入线程函数的参数(给start_routine函数传的参数)

返回值:

        成功返回0

        失败返回错误数字

#include <stdio.h>
#include "public.h"      //头文件中包含pthread_creat的头文件,但是编译时还需使用 -lpthread(链接头文件)
                         //cc src/main.c -o ./build/a.out -I./include -lpthread

void *thread_fun(void *arg)
{
	printf("线程(TID:%#x)开始执行\n", (unsigned int)pthread_self());    //pthread_self获取线程ID
	return NULL;
}

int main(int argc, const char **argv)
{
	pthread_t tid1;
	pthread_t tid2;
	pthread_t tid3;
	int ret = 0;

	ret = pthread_create(&tid1, NULL, thread_fun, NULL);
	if (ret != 0)
	{
		ERR_MSG("fail to pthread_create");
		return -1;
	}
		ret = pthread_create(&tid2, NULL, thread_fun, NULL);
	if (ret != 0)
	{
		ERR_MSG("fail to pthread_create");
		return -1;
	}
		ret = pthread_create(&tid3, NULL, thread_fun, NULL);
	if (ret != 0)
	{
		ERR_MSG("fail to pthread_create");
		return -1;
	}

	while (1)
	{
		
	}

	return 0;
}

 2、pthread_exit

man 3 pthread_exit

void pthread_exit(void *retval);

功能:

        线程退出

参数:

        retval:线程退出的值

返回值:

        缺省

3、pthread_join

man 3 pthread_join

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

功能:

        回收线程空间

参数:

        thread:要回收的线程的ID

        retval:存放线程结束状态指针空间的首地址

返回值:

        成功返回0

        失败返回错误码

注意:

        1.pthread_join具有阻塞功能,线程不结束,一直等到线程结束,回收到线程空间再继续向下执 行

        2.pthread_join具有同步功能

#include <stdio.h>
#include "public.h"

#if 0
int main(int argc,const char** argv)
{
	pthread_t tid1;
	pthread_t tid2;
	pthread_t tid3;

	pthread_create(&tid1, NULL, thread1, NULL);
	pthread_create(&tid2, NULL, thread2, NULL);
	pthread_create(&tid3, NULL, thread3, NULL);

	pthread_join(tid1, NULL);
	pthread_join(tid2, NULL);
	pthread_join(tid3, NULL);	

	return 0;
}
	
#endif

#if 0
void *thread1(void *arg)
{
	printf("线程1(TID:%#lx)正在执行\n", pthread_self());

	return NULL;
}

void *thread2(void *arg)
{
	printf("线程2(TID:%#lx)正在执行\n", pthread_self());

	return NULL;
}

void *thread3(void *arg)
{
	printf("线程3(TID:%#lx)正在执行\n", pthread_self());

	return NULL;
}

int main(int argc, const char **argv)
{
	pthread_t tid[3];
	int i = 0;
	int a[3] = {1, 2, 3};

	void *(*pfun[3])(void *) = {thread1, thread2, thread3};                 //函数指针数组

	for (i = 0; i < 3; i++)
	{
		pthread_create(&tid[i], NULL,pfun[i],NULL);
	}

	for (i = 0; i < 3; i++)
	{
		pthread_join(tid[i], NULL);
	}

#endif

#if 1
void *thread(void* arg)
{
	int* pnum = arg;

	printf("线程%d(TID:%#lx)正在执行\n", *pnum, pthread_self());

	return NULL;
}

int main(int argc, const char **argv)
{
	pthread_t tid[3];
	int i = 0;
	int a[3] = {1, 2, 3};

	for (i = 0; i < 3; i++)
	{
	  //pthread_create(&tid[i], NULL,thread,&i);                   //error 所有线程共享同一个 i 的地址,因此当线程函数 thread 访问 &i 时,获取的值可能是循环结束后 i 的最终值(即 3),而不是创建线程时的值。这会导致所有线程可能接收到相同的值3,而不是预期的 0、1、2
		pthread_create(&tid[i], NULL,thread,&a[i]);                //right 数组 a 的值是固定的({1, 2, 3}),每个线程会接收到数组 a 中对应索引的值。由于每个线程接收的是数组 a 中不同元素的地址,线程函数 thread 可以正确地访问到 1、2、3 这些值
	}

	for (i = 0; i < 3; i++)
	{
		pthread_join(tid[i], NULL);
	}
#endif

	return 0;
}

七、线程的分离属性

 1、概念

线程结束后,操作系统自动回收线程空间,无需调用pthread_join来回收线程空间

 2、函数接口

man 3 pthread_attr_setdetachstate

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

功能:

        将线程属性中加入分离属性

        在这种状态下,线程终止时会自动释放其资源,无需其他线程调用 pthread_join()

参数:

        attr:线程属性空间首地址

        detachstate: PTHREAD_CREATE_DETACHED   分离属性         PTHREAD_CREATE_JOINABLE   加入属性

返回值:

        成功返回0

        失败返回错误码

      pthread_attr_setdetachstate需要用到的辅助函数

        

        //对attr进行初始化

       int pthread_attr_init(pthread_attr_t *attr);  
       //销毁attr

        int pthread_attr_destroy(pthread_attr_t *attr);

#include <stdio.h>
#include "public.h"

#if 1
int is_exit = 0;

void* thread1(void* arg)
{
	int cnt = 0 ;
	while(!is_exit)
	{
		if(cnt == 2)
		{
			printf("采集线程正在执行!\n");
			cnt = 0;
		}
		cnt++;
		usleep(500000);
	}

	return NULL;
}
void* thread2(void* arg)
{
	int cnt = 0 ;
	while(!is_exit)
	{
		if(cnt == 4)
		{
			printf("显示线程正在执行!\n");
			cnt = 0;
		}
		cnt++;
		usleep(500000);
	}
	return NULL;
}
void* thread3(void* arg)
{
	int cnt = 0 ;
	while(!is_exit)
	{
		if(cnt == 10)
		{
			printf("存储线程正在执行!\n");
			cnt = 0;
		}
		cnt++;
		usleep(500000); 
             
	}

	return NULL;
}
void* thread4(void* arg)
{
	int cnt = 0 ;
	while(!is_exit)
	{
		if(cnt == 20)
		{
			printf("日志线程正在执行!\n");
			cnt = 0;
		}
		cnt++;
		usleep(500000);         //同样是睡眠10秒但这样的操作让单位时间缩短了,避免了线程正在睡眠时线程结束的情况
        //sleep(10)               
	}

	return NULL;
}
int main(int argc,const char** argv)
{
	int i = 0;
	pthread_t tid[4];
	pthread_attr_t attr;
	void*(*pfun[4])(void*) = {thread1,thread2,thread3,thread4};
	char tmpbuff[1024] = {0};

	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

	for(i = 0;i < 4;i++)
	{
		pthread_create(&tid[i],&attr, pfun[i], NULL);
	}

	pthread_attr_destroy(&attr);

	while(1)
	{	
		gets(tmpbuff);
		if(0 == strcmp(tmpbuff,".quit"))
		{
			is_exit = 1;
			break;
		}

	}

	sleep(1);                                 //进程睡眠1秒,确保线程完全执行过    


	return 0;
}
	
#endif


http://www.kler.cn/a/566018.html

相关文章:

  • 学术ppt模板_院士增选_自然科学奖_技术发明奖_科技进步奖_杰青_长江学者特聘教授_校企联聘长江学者_重点研发_优青_青长_青拔ppt制作案例
  • 第P6周:VGG-16-Pytorch实现人脸识别
  • 学习路程九 langchain核心组件 Chains
  • 《向华为学习:BEM战略解码》课程大纲
  • [IP] DDR_FIFO(DDR3 用户FIFO接口)
  • PyTorch 常见的损失函数:从基础到大模型的应用
  • 【北京迅为】iTOP-RK3568OpenHarmony系统南向驱动开发-第2章 内核HDF驱动框架架构
  • 咸鱼之王+手游开服搭建架设+内购修复无bug运营版
  • `maturin`是什么:matu rus in python
  • 【含文档+PPT+源码】基于地图系统的校园车辆调度系统的设计与实现
  • Python调用AnythingLLM API使用流输出
  • jenkins使用不同用户管理不同工程
  • 使用通义万相Wan2.1进行视频生成
  • FFmpeg-chapter3-读取视频流(原理篇)
  • 深入探索人工智能的未来:DeepSeek R1与蓝耘智算平台的完美结合
  • 微前端架构深度解码:模块化拆解与联邦宇宙的构建
  • 算法-二叉树篇15-最大二叉树
  • zotero软件的使用和排坑
  • python与C#相比特有的一些语法总结
  • 深入解析 Linux 内核如何解析和获取设备树属性