Linux多线程机制

本文对比了进程与线程的特点,介绍了进程创建的开销较大且进程间通信复杂等问题,提出了线程作为解决方案的优势,包括创建和切换速度快、数据交换简单等特点。并通过示例展示了线程的创建和运行。

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

       创建进程的工作本身会给操作系统带来沉重的负担,而且每个进程具有独立的内存空间,因此进程间通信的实现难度也不低。其主要缺点如下:
        1.创建进程的过程会带来一定的开销
        2.为了完成进程间数据交换,需要特殊的IPC技术
另外每秒数十次、甚至数千次的‘上限文切换’是创建进程时的最大的开销。
       单CPU系统将CPU时间分成多个微小的块后分配给了多个进程。为了分时使用CPU,需要‘上下文切换’过程。
上下文切换:运行程序前需要将相应进程信息读入内存,如果运行进程A后需要紧接着运行进程B,就应该将进程A相关信息移出内存,并读入进程B相关信息。这就是上下文切换。但此时进程A的数据将被移动到硬盘,所以上下文切换需要很长时间。

为了保持多进程的优点,同时在一定程度上克服其缺点,人们引入了线程。线程相比进程具有如下优点:
       1.线程的创建和上下文切换比进程的创建和上下文切换更快。
       2.线程间交换数据时无需特殊技术。

线程和进程的差异
       进程间的上下文切换需要复制整个内存区域,而线程则不是。每个进程的内存空间都由保存全局变量的数据区向malloc等函数的动态分配提供空间的堆函数运行时使用的栈构成,每个进程都拥有这种独立空间。而线程则是以获得多个代码执行流为目的,只分离栈区域,通过这种方式可以获得如下优势:
       1.上下文切换时不需要切换数据区和堆
       2.可以利用数据区和堆交换数据
       线程为了保持多条代码执行流而隔开了栈区域,多个线程将共享数据区和堆。为了保持这种结构,线程将在进程内创建并运行。因此进程和线程可以定义为如下形式:
进程:在操作系统构成单独执行流的单位
线程:在进程构成单独执行流的单位

线程创建及运行
       线程具有单独的执行流,因此需要单独定义线程的main函数,还需要请求操作系统在单独的执行流中执行该函数,完成该功能的函数如下:

#include<pthread.h>
int pthread_create(pthread_t *restrict thread,const pthread_attr_t *restrict attr,void *(*start_routine)(void*),void *restrict arg);
//成功时返回0,失败时返回其他值

thread---保存新创建线程ID的变量地址值。线程与进程相同,也需要用于区分不同线程的ID
attr---用于传递线程属性的参数,传递NULL时,创建默认属性的线程
start_routine---相当于线程main函数的、在单独执行流中执行的函数地址值
arg---通过第三个参数传递调用函数时包含传递参数信息的变量地址值
下面是一个示例:

#include<stdio.h>
#include<pthread.h>
void* thread_main(void *arg);

int main(int arg,char **argv)
{
	pthread_t t_id;
	int thread_param=5;
	if(pthread_create(&t_id,NULL,thread_main,(void*)&thread_param)!=0)//请求创建一个线程
	{
		puts("pthread_create() error");
		return -1;
	};
	sleep(10);//调用sleep函数延迟进程的终止时间
	puts("end of main");
	return 0; //终止进程,同时终止内部创建的线程
}
void* thread_main(void *arg) //arg参数是pthread_create函数的第四个参数
{
	int i;
	int cnt=*((int*)arg);
	for(i=0;i<cnt;i++)
	{
		sleep(1);
		puts("running thread");
	}
	return NULL;
}

     关于sleep函数的调用是控制线程的执行,相当于预测程序的执行流程。实际上是不可能预测的,因此为了准确预测thread_main函数的运行时间,利用下面的函数控制线程的执行流:

#include<pthread.h>
int pthread_join(pthread_t thread,void **status);
//成功时返回0,失败时返回其他值

thread---该参数值ID的线程终止后才会从该函数返回
status---保存线程的main函数返回值的指针变量地址值
调用该函数的进程(或线程)将进入等待状态,直到第一个参数为ID的线程终止为止。而且可以得到线程的main函数的返回值。通过下面示例了解该函数的功能:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
void *thread_main(void *arg);

int main()
{
	pthread_t t_id;
	int thread_param=5;
	void *thr_ret;
	
	if(pthread_create(&t_id,NULL,thread_main,(void*)&thread_param)!=0)//请求创建一个线程
	{
		puts("pthread_create() error");
		return -1;
	}
	if(pthread_join(t_id,&thr_ret)!=0)  //main函数将等待ID保存在t_id变量中的线程终止
	{
		puts("pthread_create() error");
		return -1;
	}
	printf("Thread return message:%s \n",(char*)thr_ret);
	free(thr_ret);
	return 0;
}
void* thread_main(void *arg)
{
	int i;
	int cnt=*((int*)arg);
	char *msg=(char*)malloc(sizeof(char)*50);
	strcpy(msg,"Hello,I am thread~ \n");
	for(i=0;i<cnt;i++){
		sleep(1);
		puts("running thread");
	}
	return (void*)msg;  //该返回值是thread_main函数内部动态分配的内存空间地址值
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值