1、并发是什么???
1.1、最通俗的理解,是指两个或两个以上的独立活动同时发生。
1.2、计算机中并发:单个系统同时执行多个独立的任务。
单个处理器实现并发的方式成为 “任务切换(task switching) ”, 实际上并不是真正的并发。 这是通过计算机内部每秒多次切换任务实现。实际上每一时刻只有一个任务被cpu执行,因为每个任务之间执行间隔时间很短暂,计算机又不断切换这些任务,所以给人感觉,这些任务就像是同时在计算机内执行。
多个处理器便可以实现真正意义上的同时执行多个独立任务,其任务数量取决于计算机处理器的数量,每个处理器之间处理任务互相独立,便可以在同一时刻让cpu执行多个独立任务,这已经是硬件上的并发了。
下面举个例子,将两个独立任务分别分成10等份,也可以想象是将每个任务所需要的时间T分为10等份。每一份执行时间即为T/10。两个任务分别用红色颜色表示,每一等份用一小方块表示,然后在双处理器和单处理器上运行的逻辑图(如下)。
从上图可以看出,双处理器分别处理两个独立的任务同时执行。 而单处理器通过“任务切换”,交替执行这两个任务,只是任务间隔时间短暂(间隔为上图灰色小方块),并不是真正同时执行。可以看出,并发效率很大程度取决于硬件。
1.3、并发途径两种途径
一种是通过单个线程的多进程,即多进程并发,另一种是单一进程中有多个线程。即多线程并发。关于进程和线程概念,仅供参考。 进程是一个可拥有资源的独立单位,线程是一个调度和分派资源的基本单位。事实证明多进程的并发所带来的系统开销远大于多线程并发,不然也就不会有线程的出现。进程的出现大概是使多个程序能够并发执行,而线程的出现则是为了减少在并发执行中所带来的系统开销。另外在C++标准并没有提供原生态对进程通信的支持,这意味着多进程实现必须要依赖平台,而且都需要程序员自己编写,C++委员会可并不是那么友好。非常幸运,C++11标准提供了对多线程的支持,可见多线程的重要性了,之前C++程序员只能使用C API 来实现多线程。
2、为什么要使用并发
关注点分离(separation of concerns )和 性能(performance)
separation of concerns: 编写软件时,分离关注点是个好主意;通过将相关的代码与无关的代码分离,可以使程序更容易理解和测试,从而减少出错的可能性。
performance: 计算能力的提高不是源自使单一任务运行的更快,而是并行运行多个任务。如何有效利用现有资源(处理器数量),充分发挥其性能,这便是并发性能的意义所有。当然,天下没有免费午餐,这也会带来其他的开销,那就是程序的复杂度了。
3、什么情况下不使用并发。
知道何时不使用并发与知道何时使用它一样重要。基本上,不使用并发的唯一原因就是,收益比不上成本。使用并发的代码在很多情况下难以理解,因此编写和维护的多线程代码就会产生直接的脑力成本,同时额外的复杂性也可能引起更多的错误。除非潜在的性能增益足够大或关注点分离地足够清晰,能抵消所需的额外的开发时间以及与维护多线程代码相关的额外成本(代码正确的前提下);否则,别用并发。
4、一个简单的Hello, Concurrent World程序
#include <iostream>
#include <thread> //①
void hello() //②
{
std::cout << "Hello Concurrent World\n";
}
int main()
{
std::thread t(hello); //③
t.join(); //④
}
#include <thread> ①,标准C++库中对多线程支持的声明在新的头文件中:管理线程的函数和类在 <thread> 中声明,而保护共享数据的函数和类在其他头文件中声明。其次,写信息的代码被移动到了一个独立的函数中②。因为每个线程都必须具有一个初始函数(initial function),新线程的执行在这里开始。对于应用程序来说,初始线程是main(),但是对于其他线程,可以在 std::thread 对象的构造函数中指定——在本例中,被命名为t③的 std::thread 对象拥有新函数hello()作为其初始函数。与直接写入标准输出或是从main()调用hello()不同,该程序启动了一个全新的线程来实现,将线程数量一分为二——初始线程始于main(),而新线程始于hello()。新的线程启动之后③,初始线程继续执行。如果它不等待新线程结束,它就将自顾自地继续运行到main()的结束,从而结束程序——有可能发生在新线程运行之前。这就是为什么在④这里调用 join() 的原因——详见第2章,这会导致调用线程(在main()中)等待与 std::thread 对象相关联的线程,即这个例子中的t。如果这看起来仅仅为了将一条信息写入标准输出而做了大量的工作,确实如此——正如上文一般来说并不值得为了如此简单的任务而使用多线程,尤其是在这期间初始线程并没做什么。本书后面的内容中,将通过实例来展示在哪些情景下使用多线程可以获得明确的收益。
《C++ Concurrent In Action》: 《C++ 并发编程》- 第1章 你好,C++的并发世界