并发与并行

并发

并发(Concurrence):指两个或两个以上的事件或活动在同一时间间隔内发生。并发的实质是单个物理 CPU(也可以多个物理CPU) 在若干道程序之间多路复用,并发可以对有限物理资源强制行使多用户共享以提高效率。

逻辑上来说,执行实例是并发执行,但是物理上仍是串行执行

并行

并行(Parallelism)指两个或两个以上事件或活动在同一时刻发生。在多道程序环境下,并行性使多个程序同一时刻可在不同CPU上同时执行

C++ 并行

C++11 新标准中引入了五个头文件来支持多线程编程,它们分别是 <atomic>, <thread>, <mutex>, <condition_variable><future>

一个简单的例子:

#include <bits/stdc++.h> 
#include <thread>
using namespace std;

int e = 0;

void f() {
	e++;
	cout << "e is " << e << endl;
}

int main() {    

    for (int i = 0; i < 20; i++) {
    	std::thread t(f);
		t.join();
    }
	
	return 0;
}

一个新的线程通过构建 std::thread 对象而启动

#include <bits/stdc++.h> 
#include <thread>
#include <vector>
using namespace std;

int e = 0;

bool finished = false;

void f() {
	e++;
	cout << "test " << e << endl;
}


int main() {    

	vector<thread> v;
    for (int i = 0; i < 20; i++) {
    	v.emplace_back(f);
    }

    for (auto& it:v)
    	it.join();

	return 0;
}

新线程启动后,起始线程继续执行,如果起始线程不等待新线程结束,就会执行到main最后。可能新线程根本没机会启动,例子中用join让主线程等待新线程

join

加入一个新线程,主线程等待新线程结束

只要调用过join,线程就不可再汇合(joinable)

detach

不需要等待线程结束,将其分离,无法等待新线程完结

线程在后台运行,无法直接通信。然而分离的线程确实仍在后台运行,其归属权和控制权交给了C++运行时库,一旦线程退出,与之关联的资源都会被正确回收

在thread中传递参数

传递参数的方式很简单,直接向std::thread的构造函数增添更多参数即可

注意:thread的构造函数原样复制所提供的值,并没有转换为预期的参数类型引用的传参

所以要传引用的时候,应该显式地使用std::ref

void update(int &data)  //expects a reference to int
{
    data = 15;
}
int main()
{
    int data = 10;

    // This doesn't compile as the data value is copied when its reference is expected.
    //std::thread t1(update, data);         

    std::thread t1(update, std::ref(data));  // works

    t1.join();
    return 0;
}

类似的例子还有string:

#include <bits/stdc++.h> 
#include <thread>
#include <vector>
using namespace std;

void f(string const &s) {
	cout << "s = " << s << endl;
}

int main() {    

	std::thread t(f, string("test"));
	t.join();
	return 0;
}

右值引用:C++11中增加了右值引用,右值引用关联到右值时,右值被存储到特定位置,右值引用指向该特定位置,也就是说,右值虽然无法获取地址,但是右值引用是可以获取地址的,该地址表示临时对象的存储位置

C++ 标准规定,临时对象只能绑定到 const 左值引用或右值引用,而不能绑定到非常量的左值引用