并发的概念:
两个或更多独立的活动同时发生
假并发与真并发:
1、任务切换(任务调度): 此种方式发生在只有单核心单处理器的老式计算机上。依赖处理器较高的速度不停的在各种任务间切换,以实现”并发”(并不是真正意义上的并发,在任意时刻仅在执行一个任务)
2、硬件并发: 此种方式发生在拥有多处理器或多核处理器的计算机上。依靠硬件的支持,能够实现正真意义上的同时执行多个任务
显然,在同一条件下,后者绝对优于前者;而且前者的执行完所有任务的时间也不是后者执行各任务时间的简单相加。这是因为切换任务时需要切换一次上下文。
切换上下文:
1、系统必须为当前运行的任务保存CPU的状态和指令指针(以便以后恢复现场)
2、计算出接下来需要切换的任务,并为即将切换的任务重新加载处理器状态
3、可能需要将内存中新任务的指令和数据加载到缓存中(缓存未命中),这会阻止CPU执行任何指令
由此可见,要实现高并发,应该尽量避免上下文的切换,尽量依靠硬件并发,也就是说硬件线程的数量是决定最大并发数量的决定因素。
然而,在显示生活中,很可能出现需要执行的任务数比硬件线程还多的情况,这种情况下,任务切换依旧不可避免。
并发的途径
1、多进程并发: 将程序分为多个独立进程,并使他们在同一时刻运行
2、多线程并发: 在单个进程中运行多个线程
两种并发的优缺点对比
对于多进程并发:
1、优点:
可以通过远程连接(可能需要通过网络)的方式来实现在两台电脑上并发
2、缺点:
1)、并发的同步困难(出于安全考虑,一般进程间的地址空间是相互独立的,只有借助系统并通过进程间的通信(如信号、套接字、管道、文件等)手段进行通信,但是进程间通信的手段不是设置复杂就是速度慢)
2)、运行多个进程所需的开销大: 包括进程的启动时间、系统需要内部资源来管理进程,等
对于多线程并发:
1、优点:
1)、每个线程可以相互独立的运行
2)、易于通信,同一进程中的所有线程共享地址空间,全局性质(进程级)的资源可以被所有线程访问
2、缺点:
当同一数据需要被多个线程访问时,必须做好同步(至于为什么线程间的同步是必须的,请移步我的另一篇博客)
对比
1、多线程比多进程有更容易且高效的通信方式
2、进程间、线程间都可以独立的运行
3、多线程方式比多进程方式所需要的开销更小(由于系统缺少对线程间数据的主动保护,系统记录的工作量更小)
4、进程间相互独立,同一进程中线程共享全局性质资料
5、线程间上下文的切换比进程间上下文的切换快得多
为什么使用并发(多线程)
1、关注分离点: 分离相关代码与无关代码,合理的分离使代码更容易编写、理解(如全双工通信时,可以将输入与输出相分离,每次仅仅考虑输入或输出一项,实际上此处的分离也带来了性能的提升)
2、提升性能
并发提升性能的两种方式
1)、任务并行方式 将单个任务分成几个部分,并各自并行运行
2)、可并行方式 在同一时间段内同时处理多个相同的事务
前一种方式对于一些易并行的算法(一个线程执行算法的一部分,另一个线程执行算法的另一部分)来说是非常好的,因为当硬件线程数量增加时,算法的并行性也会增加。后一种方式则会带来吞吐量的提升
什么时候不使用并发(多线程)
这要从使用线程的缺点说起(注意此处仅仅是从线程的角度):
带来的性能的增益可能会小于预期,收益比不上成本
1)、系统需要给线程分配内核相关资源和栈空间,如果太多线程同时运行会消耗系统资源,可能使得系统整体运行更加缓慢
2)、新线程的启动以及将新线程加入调度器需要时间,线程本身的执行任务的时间可能小于产生线程的时间
3)、线程越多,系统所需的上下文切换动作就越多,上下文切换会占用任务执行时间
4)、可能使代码复杂化,不利于理解、调试、甚至更容易出错
因此,除非潜在的性能增益足够大或关注点分离地足够清晰,以至于能抵消所需的额外的开发时间以及与维护多线程代码相关的额外成本(代码正确的前提下);否则,别用并发。
C++11中的多线程
当C++标准库没有提供所需的性能或行为时,就有必要使用平台相关的工具。
在C++线程库中提供一个 native_handle() 成员函数,允许通过使用平台相关API直接操作底层实现
参考:C++并发编程实战