Linux线程

本文介绍了线程的概念,指出在Linux中线程由进程模拟。阐述了线程与进程的区别,包括地址空间、通信关系、切换调度等方面。还说明了线程的状态,如就绪、阻塞、运行。同时列举了线程的优缺点,并给出创建线程的函数原型及参数说明,介绍了进程ID和线程ID的相关概念。

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

线程

线程
也称轻量级进程,是程序执行流的最小单元。而多线程就是指,在一个进程中有多个执行流,在同时执行。进程是承担调度的基本单位,一个进程可拥有多个线程,它的执行力度比进程更加细致,线程资源共享。

注意
在Linux中并不存在真正的线程,Linux测线程是使用进程模拟的。我们在Linux系统中,线程的创建实在内核外进行的,有POSIX提供的线程库实现。因此链接这些线程函数库时要使用编译器命令的“-lphtread”选项。

线程的本质
在Linux中,新建的线程并不是在原先的进程中,而是系统通过一个系统调用clone(),该系统copy了一个和原进程完全一样的进程,并在这个进程中执行线程函数,不过这个copy过程和fork不一样,copy后的进程和原来的进程共享了所有的变量,运行环境。所以线程是共享全局变量和环境的。

线程和进程的区别
进程是资源竞争的基本单位,线程是程序执行的最小单位,是承担调度的基本单位。操作系统中每一个执行的进程,都有它自己的地址空间,而同一进程中可以有多个线程,也就是多个执行流在同时执行。这里的同时,如果是单核处理器,则并不是真正意义上的同时,由于处理器运行速度很快,给每个执行流分配了时间片,在单核处理器微观上还是顺序执行,而在多核处理器中,就是真正意义上的并行。由于同一进程的多个线程共享同一个地址空间,因此线程之间有互相共享资源。但是也拥有自己独立的部分:线程ID,独立的上下文数据,栈,errno,信号屏蔽字,调度优先级。

线程之间共享的资源主要有:
1.地址空间
2.数据段和代码段
3.全局变量
4.文件描述符表
5.信号处理方式(忽略或者有自定义动作)
6.用户ID和组ID
7.当前工作目录

线程独享资源主要有:
1.线程ID
2.上下文,包括各种寄存器的值,程序计数器和栈指针
3.栈空间
4.errno变量
5.信号屏蔽字
6.调度优先级

线程与进程的区别归纳如一下几点:
1.地址空间:进程间相互独立,每个进程都有自己独立的地址空间,同一进程的各线程间共享地址空间。某个进程内的线程在其他进程内不可见。
2.通信关系:进程间通信有管道,消息队列,共享内存,信号量。线程间通信可以直接读写全局变量来进行通信。不管是进程还是线程,通信时可能出现数据不一致的情况,需要用同步互斥机制来保证数据的一直性。
3.切换和调度:由于进程间独占数据段代码等信息,所以切换进程的时候,需要把进程间独占的资源切换出去,把需要执行的进程资源换进来,而线程的进程的子集,共享大部分资源,切换时只需要保存上下文相关信息就好,所以线程切换的开销比进程切换的开销小。
4.线程是处理器调度的基本单位,但进程不是
5.二者均可并发执行

线程的状态
就绪:线程具备运行的所有条件,逻辑上已可以运行,在等待处理机
阻塞:指线程在等待某一时间的发生,如I/O操作
运行:占有处理机正在运行

线程的优点:
1.创建进程消耗的资源更少
2.线程切换的开销更少
3.充分利用多处理器的可并行数量
4.线程占用的资源比进程少
5.在等待慢速I/O操作结束的同时,程序可以执行其他的就算任务
6.计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
7.I/O密集型应用,为了能提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。

线程的缺点
1.如果计算密集型线程比可用处理器多,有可能增加了额外的同步和调度开销,而可用的资源不变
2.健壮性降低:编写多线程程序需要考虑的更加全面,有可能共享了不该共享的变量,造成不良影响
3.缺乏访问控制:进程是访问控制的基本粒度,在一个线程中调用某些系统函数对整个进程造成影响
4.编写程序难度较高

主要的函数列表
在这里插入图片描述
在这里插入图片描述

创建线程
功能:创建一个线程
原型:
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);

参数:
thread返回线程ID;
attr设置线程属性,默认为NULL;
start_routine一个函数地址,线程启动后执行的函数;
arg传给执行的函数的参数
返回值:成功返回0,失败返回错误码。

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

void *func(void *arg)
{

	int n = *(int*)(arg);

	while (1)
	{
		printf("child thread n = %d\n", n);
		sleep(1);
	}
}


int main()
{
	int n = 5;

	pthread_t tid;
	int ret;
	ret = pthread_create(&tid, NULL, func, (void*)&n);

	if (ret != 0)
	{
		fprintf(stderr, "pthread_create:%s\n", strerror(ret));
		exit(-1);
	}

	while (1)
	{
		printf("main thread\n");
		sleep(1);
	}
}

进程ID和线程ID
在Linux中,目前的线程是靠POSIX线程库和进程实现的,在这种实现下,线程又被称为轻量级进程,每一个用户态的线程,在内核中都有一个对应的调度实体,也有自己的task_struct结构体。
  在没有线程之前,一个进程对应内核里的一个task_struct,对应一个进程ID;但是在引入线程之后,一个进程下有n个用户态线程,每个线程作为独立的调度实体有自己的进程描述符,这样,进程和内核中的进程描述符变成了1:n关系,但是POSIX标准要求所有的线程调用getpid时返回相同的进程ID。此时,就有了线程组的概念。
struct task_struct {

pid_t pid;
pid_t tgid;

struct task_struct *group_leader;

struct list_head thread_group;

};
多线程的进程,又被称为线程组,线程组内的每一个线程在内核中存在一个进程描述符与之对应。其实在进程描述符中pid,描述的线程ID,其中的tgid对应的才是用户层的进程ID。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值