线程与进程的优劣对比
文章目录
- 前言
- 一、线程与进程的对比
- 1.两者的优劣对比
- 2.使用多线程的理由
- 二、线程编程实战
- 1.相关api的介绍
- 2.初级实战代码1
- 3.初级实战代码2
- 总结
前言
线程与进程在liunx系统编程中都是非常重要的一部分,并且两者有很多相似的地方,也有许多不同的地方,今天我们来比较一下两者,并做一下线程编程实战
一、线程与进程的对比
1.两者的优劣对比
UNIX/Linux进程可以看成只有一个控制线程:一个进程在同一时刻只做一件事情。有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事,每个线程各自处理独立的任务。
进程是程序执行时的一个实例,是担当分配系统资源(CPU时间、内存等)的基本单位。在面向线程设计的系统中,进程本身不是基本运行单位,而是线程的容器。程序本身只是指令、数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实例。
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。线程包含了表示进程内执行环境必须的信息,其中包括进程中表示线程的线程ID、一组寄存器值、栈、调度优先级和策略、信号屏蔽字、errno常量以及线程私有数据。进程的所有信息对该进程的所有线程都是共享的,包括可执行的程序文本、程序的全局内存和堆内存、栈以及文件描述符。在Unix和类Unix操作系统中线程也被称为轻量级进程(lightweight processes),但轻量级进程更多指的是内核线程(kernel thread),而把用户线程(user thread)称为线程。
"进程——资源分配的最小单位,线程——程序执行的最小单位"
进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
2.使用多线程的理由
从上面我们知道了进程与线程的区别,其实这些区别也就是我们使用线程的理由。总的来说就是:进程有独立的地址空间,线程没有单独的地址空间(同一进程内的线程共享进程的地址空间)。
使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右,当然,在具体的系统上,这个数据可能会有较大的区别。
使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。
除了以上所说的优点外,不和进程比较,多线程程序作为一种多任务、并发的工作方式,当然有以下的优点:
提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。
二、线程编程实战
1.相关api的介绍
线程创建
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
// 返回:若成功返回0,否则返回错误编号
当pthread_create成功返回时,由tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于定制各种不同的线程属性,暂可以把它设置为NULL,以创建默认属性的线程。
新创建的线程从start_rtn函数的地址开始运行,该函数只有一个无类型指针参数arg。如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入。线程退出
单个线程可以通过以下三种方式退出,在不终止整个进程的情况下停止它的控制流:
1)线程只是从启动例程中返回,返回值是线程的退出码。
2)线程可以被同一进程中的其他线程取消。
3)线程调用pthread_exit:
#include <pthread.h>
int pthread_exit(void *rval_ptr);
rval_ptr是一个无类型指针,与传给启动例程的单个参数类似。进程中的其他线程可以通过调用pthread_join函数访问到这个指针。线程等待
#include <pthread.h>
int pthread_join(pthread_t thread, void **rval_ptr);
// 返回:若成功返回0,否则返回错误编号
调用这个函数的线程将一直阻塞,直到指定的线程调用pthread_exit、从启动例程中返回或者被取消。如果例程只是从它的启动例程返回i,rval_ptr将包含返回码。如果线程被取消,由rval_ptr指定的内存单元就置为PTHREAD_CANCELED。
可以通过调用pthread_join自动把线程置于分离状态,这样资源就可以恢复。如果线程已经处于分离状态,pthread_join调用就会失败,返回EINVAL。
如果对线程的返回值不感兴趣,可以把rval_ptr置为NULL。在这种情况下,调用pthread_join函数将等待指定的线程终止,但并不获得线程的终止状态。
4线程ID获取及比较
#include <pthread.h>
pthread_t pthread_self(void);
// 返回:调用线程的ID
2.初级实战代码1
#include <stdio.h>
#include <pthread.h>void *func1(void *arg)
{// static int ret=10;//检验退出函数pthread_exit能否返回整数static char *p="wencai love pingping";//检验退出函数pthread_exit能否返回字符串printf("t1:%ld thread is creat\n",(unsigned long)pthread_self());//输出进程id号printf("t1 param is %d\n",*((int *)arg));pthread_exit((void *)p);//主动退出进程
}void *func2(void *arg)
{//static int ret=10;//检验退出函数pthread_exit能否返回整数static char *p="wencai love pingping";//检验退出函数pthread_exit能否返回字符串printf("t2:%ld thread is creat\n",(unsigned long)pthread_self());//输出进程id号printf("t2 param is %d\n",*((int *)arg));pthread_exit((void *)p);//主动退出进程}int main()
{int ret;int ret1;int param=100;char *pret=NULL;pthread_t t1;pthread_t t2;ret=pthread_create(&t1,NULL,func1,(void *)¶m);//创建进程if(ret==0){printf("main:creat t1 success\n");}ret1=pthread_create(&t2,NULL,func2,(void *)¶m);if(ret1==0){printf("main:creat t2 success\n");}printf("main:%ld\n",(unsigned long)pthread_self());pthread_join(t2,(void **)&pret);//等待进程开始,防止主线程结束,导致子线程提前结束。printf("main:t1 quit %s\n",pret);//检验返回的字符串printf("main:t2 quit %s\n",pret);return 0;
}
看看在liunx平台下的运行效果
可以看到两个线程都成功创建了,但不能体现线程的存储空间是共享的。因而下面的代码将能体现这个特点。
3.初级实战代码2
#include <stdio.h>
#include <pthread.h>int data=0;//定义一个全局变量,用于检验线程存储空间的共享void *func1(void *arg)
{// static int ret=10;static char *p="wencai love pingping";printf("t1:%ld thread is creat\n",(unsigned long)pthread_self());printf("t1 param is %d\n",*((int *)arg));while(1){//利用死循环让线程打印多个全局变量的值,从运行结果来看,可以知道每次的值都是不连续的,因而证明了线程的存储空间是共享的。printf("t1:data=%d\n",data++);sleep(1);}}void *func2(void *arg)
{//static int ret=10;static char *p="wencai love pingping";printf("t2:%ld thread is creat\n",(unsigned long)pthread_self());printf("t2 param is %d\n",*((int *)arg));while(1){//利用死循环让线程打印多个全局变量的值,从运行结果来看,可以知道每次的值都是不连续的,因而证明了线程的存储空间是共享的。printf("t2:data=%d\n",data++);sleep(1);}}int main()
{int ret;int ret1;int param=100;pthread_t t1;pthread_t t2;ret=pthread_create(&t1,NULL,func1,(void *)¶m);if(ret==0){printf("main:creat t1 success\n");}ret1=pthread_create(&t2,NULL,func2,(void *)¶m);if(ret1==0){printf("main:creat t2 success\n");}printf("main:%ld\n",(unsigned long)pthread_self());while(1){//利用死循环让线程打印多个全局变量的值,从运行结果来看,可以知道每次的值都是不连续的,因而证明了线程的存储空间是共享的。printf("main:data=%d\n",data++);sleep(1);}pthread_join(t1,NULL);pthread_join(t2,NULL);return 0;
}
再来看看运行效果
可以看到两个线程对于全局变量data是共享的,这从而证明了线程存储空间是共享的
总结
对于上面线程相关的demo,我们可以看到,两个线程之间是相互竞争的,我们不知道哪个线程先执行,也无法操控线程运行的先后,因而在后续的博文中,将会引入条件变量和互斥量来实现对线程的控制。
线程与进程的优劣对比相关推荐
- 操作系统/应用程序、操作中的“并发”、线程和进程的区别,线程
并发编程前言: 1.网络应用 1)爬虫 直接应用并发编程: 2)网络框架 django flask tornado 源码-并发编程 3)socketserver 源码-并发编程 2.运维领域 1)自动 ...
- 线程与进程的控制原语对比
线程与进程的控制原语对比 fork pthead_create exit( int ) pthead_exit(void *); wait(int *) pthread_join( ,void **) ...
- 线程和进程的对比之优缺点
进程的概念 首先说一下程序,计算机程序只是存储在磁盘上的可执行的二进制(或其他类型的)文件,只有把他们加载到内存中并被系统调用,才拥有自己的生命周期进程(又被称为重量级进程)则是一个可执行的程序.每一 ...
- java php 性能比较_JAVA和PHP的优劣对比
原标题:JAVA和PHP的优劣对比 这样从几个方面来看: 一.运行机制:Java代码被编译成字节码后,会在虚拟机里由JIT进行二次编译成为本地码,据传言其执行速度可以和C++相媲美,经过我自己测试,用 ...
- 为什么校招面试中总被问“线程与进程的区别”?我该如何回答?
作者 | 宇宙之一粟 责编 | 徐威龙 出品 | AI 科技大本营(rgznai100) 进程与线程?(Process vs. Thread?) 面试官(正襟危坐中):给我说说"线程&quo ...
- 线程 、进程、协程 三者区别
从计算机硬件角度: 计算机的核心是CPU,承担了所有的计算任务. 一个CPU,在一个时间切片里只能运行一个程序. 从操作系统的角度: 进程和线程,都是一种CPU的执行单元. 进程:表示一个程序的上下文 ...
- python线程池模块_Python并发编程之线程池/进程池--concurrent.futures模块
一.关于concurrent.futures模块 Python标准库为我们提供了threading和multiprocessing模块编写相应的多线程/多进程代码,但是当项目达到一定的规模,频繁创建/ ...
- 《转载》Python并发编程之线程池/进程池--concurrent.futures模块
本文转载自 Python并发编程之线程池/进程池--concurrent.futures模块 一.关于concurrent.futures模块 Python标准库为我们提供了threading和mul ...
- 线程、进程、多线程、多进程和多任务有啥关系?
点击上方"朱小厮的博客",选择"设为星标" 后台回复"书",获取后台回复"k8s",可领取k8s资料 可能学习操作系统开 ...
最新文章
- layui 页面保存数据
- 苹果手机各种型号图片_2020年12月小米/红米手机推荐|小米/红米手机选购要点|小米/红米手机性价比推荐,干货满满...
- 内存问题提醒!结构体+protobuf做协议体发送!序列化
- 五大软件设计原则学习笔记3——Liskov 替换原则
- linux编译时开启宏,【spec】Linux上编译安装的spec文件中的常用路径以及宏变量
- a = b(将 b 赋值给 a 的另类实现)
- [转]老婆还是自己好
- c语言log_C语言最大难点揭秘~!
- day11_界面闪烁处理
- 地理信息系统(GIS)系列——ArcGIS API for JavaScript 3.9(2)
- 桌面壁纸所放位置+魔镜壁纸的下载方法
- HTML多人联机游戏,前端实现双人联机版俄罗斯方块小游戏2(实现双人联机)
- Python3对股票数据进行分析
- sql做题记录(一)
- 扬声器程序设计(微机原理实验四)
- PhpSpreadsheet中文文档 | Spreadsheet操作教程实例
- php word权限设置密码,在php中加密和解密word docx文件的问题
- (刘二大人)PyTorch深度学习实践-卷积网络(Advance)
- 316级和304级不锈钢有什么区别?
- 什么是中间件,中间件有什么作用
热门文章
- 爬楼梯——递推法(一维、多维、图)
- 新春佳节来临之际(博客之星评选结束后),我决定拿出所有家当来回馈一波 CSDN 上的 16 万读者,有 2020 年最新款 MacBook Air,有 AirPower Pro
- 计算机二级白盒测试和黑盒测试,白盒测试和黑盒测试
- 理解加密资产中的验证者两难
- DIYer 教你如何选购键盘
- 快速排序计算第K大的数
- 微信小程序 设置背景音频
- java获取f5服务器真实ip,F5服务器做负载均衡时WebService获取真实客户端IP地址
- sort排序方法的实现原理
- 普罗米修斯监控系统_一步步教你用Prometheus搭建实时监控系统系列(二)——详细分析拉取和推送两种不同模式...