C#并行编程(1):理解并行
什么是并行
并行是指两个或者多个事件在同一时刻发生。
在程序运行中,并行指多个CPU核心同时执行不同的任务;对于单核心CPU,严格来说是没有程序并行的。并行是为了提高任务执行效率,更快的获取结果。
与并发的区别:
并发是指两个或者多个事件在同一时段发生。
相对于并行,并发强调的是同一时段,是宏观上的同时发生。实际上,同一时刻只有一个任务在被执行,多个任务是分时地交替执行的。并发是为了更合理地分配资源。

如何实现并行
并行编程中我们只关注应用层面的并行,CPU的指令并行技术(指令流水等)不在我们的考虑范围。
从并行的意义来看,并行编程的目的无非是让多个CPU核心同时执行不同业务逻辑,获取优良的性能。但是,要怎样实现并行呢?实现并行,我们要借助进程和线程。
为了更好地管理计算机中运行的程序,计算机操作系统引入进程:
狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。
广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。
——百度百科
由于进程拥有计算机资源,在创建、切换和撤销的过程中开销较大,这就限制了进程的并发程度;多核CPU的日渐普及的环境下,为提高并行粒度和并行计算的效率,引入了一种轻型的进程——线程:
线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
——百度百科
线程包含于进程,同一进程的线程共享该进程的资源。线程出现后,线程取代进程作为操作系统调度和分派的基本单位,极大地减少了进程切换带来的性能损失,使得更细粒度和更高性能的并行得以实现。
进程的调度
一台计算机会运行很多程序,这些程序进程的数量多会大于CPU的核心数量。每个CPU核心同一时间只能执行一个进程,那操作系统是如何管理这些进程的呢?
当启动一个程序的实例时,操作系统将创建一个进程用来调度该程序实例。一个进程主要包含以下的信息:
进程控制块PCB,用于操作系统控制该程序实例
- 进程标识信息,如PID、名称等
- 现场信息,存放进程运行时处理器现场信息
- 控制信息,存放操作系统用于管理和调度进程的信息
- 专有的虚拟地址空间
- 句柄列表
- 程序实例的代码和数据,被映射到进程私有虚拟地址空间
程序状态字信息
进程的状态模型,如下图:

操作系统按照进程状态进行程序调度。
- 启动程序时,操作系统创建进程,此时进程为
新建态- 运行资源充足时,操作系统提交进程到
就绪状态,等待CPU选择或者抢占CPU执行 - 运行资源不足,如主存不够,操作系统会挂起进程,进程状态改为
就绪挂起,等待操作系统的恢复
- 运行资源充足时,操作系统提交进程到
- 就绪状态的进程
- CPU空闲时,会选择执行就绪状态的进程,被选中的进程进入
运行状态 - 进程优先级高时,将抢占当前正在执行进程的CPU资源,自身进入运行状态
- 操作系统会根据当前的可用资源,把就绪状态的进程挂起
- CPU空闲时,会选择执行就绪状态的进程,被选中的进程进入
- 就绪挂起的进程
- 当前没有就绪的进程,或者就绪挂起的某个进程具有较高的优先级,操作系统会将就绪挂起的进程恢复到就绪状态
- 运行状态的进程
- 进程自然结束、被强制终结或者出现无法解决的异常,将进入
终止状态,终止的线程不再参与进程调度 - 进程到达运行的时间片或者出现优先级高的进程抢占了CPU,进程会回到就绪状态等待调度
- 进程等待资源、I/O或者信号时,会进入
阻塞状态 - 优先级较高的进程抢占CPU,而此时系统资源不足,则正在运行的线程会被转入就绪挂起状态
- 进程自然结束、被强制终结或者出现无法解决的异常,将进入
- 阻塞状态的进程
- 进程阻塞的条件被满足,如等待的资源到位、I/O完成或收到信号,会进入就绪状态
- 进程在等待资源、I/O或者信号时,若系统检测到运行资源不足,会将阻塞的进程挂起进入
阻塞挂起状态
- 阻塞挂起的进程
- 当被挂起的进程具有较高优先级,同时由于其他进程的退出使资源充裕,进程会被转为阻塞状态
- 挂起的阻塞进程得到资源、I/O完成或者收到信号后,被转入就绪挂起状态
上述便是进程的调度过程,其中挂起的进程不占有任何资源。进程的调度很大程度是依赖于运行资源的;进程的优先级也是影响进程调度的重要因素;此外进程的调度还会涉及进程间的通信和同步问题,这里不做展开。
实际上,相对于进程,在并行编程中我们更关心线程,因为线程才是系统调度的基本单位。
线程的调度
在Windows系统中,每个进程至少有一个线程,每个线程都包含下面的内容:
- 线程内核对象,包含线程上下文(包含CPU寄存器信息的内存块)
- 线程环境块,包含线程的异常处理链首、本地存储数据等
- 用户模式栈,存储传给方法的局部变量和实参
- 内核模式栈,线程调用操作系统内核函数时,所传实参从用户模式栈复制到内核模式栈
- DLL线程连接和分离,线程创建和销毁时,所依赖的DLL需要收到通知才能执行相关资源的初始化和清理
从线程所含内容,我们可以知道线程的创建和销毁是有着时间和空间开销的,虽然这些开销相较于进程来说小了很多,但仍是影响程序效率的重要因素。特别是在并行处理的时候,线程的频繁创建和销毁将对并行性能产生极为严重的影响。
系统同一时间只给一个CPU核心分配一个线程,CPU执行该线程达一个时间片后,系统会给该CPU核心分配另一个线程。系统分配线程至CPU核心的过程就是线程的上下文切换过程,此间,系统将执行3个动作:
- 把CPU寄存器的值保存到正在运行的线程上下文中
- 从现有线程集合中选取一个线程准备分配
- 把选中线程上下文中保存的CPU寄存器值加载到CPU寄存器中
线程上下文切换会对程序性能带来很严重的影响,特别是切换到一个新进程的新线程时,很可能需要从RAM中加载代码和数据,大家知道RAM相对于CPU高速缓存太慢了。
线程的创建、切换及销毁都是有着不可忽视的开销,在追求高性能的程序中,我们应尽量少地线程,最优性能的线程数是机器CPU的核心数。当然,性能只是程序的一个方面,响应性和可靠性也是要关注的重点。
小结
并行在进程层面依赖于系统可用系统资源和CPU核心数,单核CPU的程序并行,实质上是并发;在线程层面则主要依赖于CPU核心数以及我们安排线程的方式。
后续将以.NET为例总结并发编程。
注:本文关于进程和线程的相关内容以Windows操作系统为参考。
C#并行编程(1):理解并行的更多相关文章
- OpenCL学习笔记(二):并行编程概念理解
欢迎转载,转载请注明:本文出自Bin的专栏blog.csdn.net/xbinworld. 技术交流QQ群:433250724,欢迎对算法.技术.应用感兴趣的同学加入. 并行编程的需求是显而易见的,其 ...
- C#并行编程--命令式数据并行(Parallel.Invoke)
命令式数据并行 Visual C# 2010和.NETFramework4.0提供了很多令人激动的新特性,这些特性是为应对多核处理器和多处理器的复杂性设计的.然而,因为他们包括了完整的新的特性,开 ...
- C#并行编程之数据并行
所谓的数据并行的条件是: 1.拥有大量的数据. 2.对数据的逻辑操作都是一致的. 3.数据之间没有顺序依赖. 运行并行编程可以充分的利用现在多核计算机的优势.记录代码如下: public class ...
- 五 浅谈CPU 并行编程和 GPU 并行编程的区别
前言 CPU 的并行编程技术,也是高性能计算中的热点,也是今后要努力学习的方向.那么它和 GPU 并行编程有何区别呢? 本文将做出详细的对比,分析各自的特点,为将来深入学习 CPU 并行编程技术打下铺 ...
- 第五篇:浅谈CPU 并行编程和 GPU 并行编程的区别
前言 CPU 的并行编程技术,也是高性能计算中的热点,也是今后要努力学习的方向.那么它和 GPU 并行编程有何区别呢? 本文将做出详细的对比,分析各自的特点,为将来深入学习 CPU 并行编程技术打下铺 ...
- C#并行编程--命令式数据并行(Parallel.Invoke)---与匿名函数一起理解(转载整理)
命令式数据并行 Visual C# 2010和.NETFramework4.0提供了很多令人激动的新特性,这些特性是为应对多核处理器和多处理器的复杂性设计的.然而,因为他们包括了完整的新的特性,开 ...
- C#并行编程-Task
菜鸟学习并行编程,参考<C#并行编程高级教程.PDF>,如有错误,欢迎指正. 目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 ...
- C#并行编程-线程同步原语
菜鸟学习并行编程,参考<C#并行编程高级教程.PDF>,如有错误,欢迎指正. 目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 ...
- C#并行编程-PLINQ:声明式数据并行
目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 C#并行编程-线程同步原语 C#并行编程-PLINQ:声明式数据并行 背景 通过LINQ可 ...
- C#并行编程-PLINQ:声明式数据并行-转载
C#并行编程-PLINQ:声明式数据并行 目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 C#并行编程-线程同步原语 C#并行编程-P ...
随机推荐
- 使用flask_socketio实现服务端向客户端定时推送
websocket连接是客户端与服务器之间永久的双向通信通道,直到某方断开连接. 双向通道意味着在连接时,服务端随时可以发送消息给客户端,反之亦然,这在一些需要即时通讯的场景比如多人聊天室非常重要. ...
- C++11模板友元语法
第 1 类: 普通类A的 普通类B 友元(一对一友好关系): 无需前置声明class B,当class B第一次出现在friend声明中时,该名字被隐式地认为可见. class A { friend ...
- 2017-2018-2 165X 『Java程序设计』课程 助教总结
2017-2018-2 165X 『Java程序设计』课程 助教总结 本学期完成的助教工作主要包括: 编写300道左右测试题,用于蓝墨云课下测试: 发布博客三篇:<2017-2018-2 165 ...
- Levmar 配置
Levmar配置 原文有些错误,在我的博客里已经改好了:http://blog.sina.com.cn/s/blog_45b747f70101he1t.html 如果6或者7自由度机器人的运动学逆解无 ...
- MAC上mongodb连接不上
1.在Mac客户端里输入 mongo,发现mongo连接不上了,原因是mongo的服务没有开启. 2.在命令行了输入 mongod,开启服务的命令 3.启动起来以后,用mongo连接服务器.
- 【转】Python中的运算符
[转]Python中的运算符 说完常用的数据类型,再来说下运算符.运算符用于将各种类型的数据进行运算,让静态的数据跑起来. 编程语言中的运算大致分为以下几个大类: 算术运算, 用于加减乘除等数学运算 ...
- openstack swift节点安装手册3-最后的安装配置及验证
以下步骤都在controller节点上执行 1.远程获取/etc/swift/swift.conf文件: curl -o /etc/swift/swift.conf https://git.opens ...
- quart源码阅读(一)
def run( self,host: str='127.0.0.1',port: int=5000,ssl: Optional[SSLContext]=None,debug: Optional[bo ...
- Android中PopupWindow用法
参考资料链接:http://developer.android.com/reference/android/widget/PopupWindow.html 在Android中有很多级别的Window, ...
- java.lang.NoClassDefFoundError: com/google/inject/Injector
报错如下: 解决方案: <dependency> <groupId>com.google.inject</groupId> <artifactId>gu ...