力扣1114 https://leetcode.cn/problems/print-in-order/solution/c-hu-chi-suo-tiao-jian-bian-liang-xin-hao-liang-yi/

1、互斥量

// 保证俩代码不会同时执行
// c++11并发与多线程视频课程_2_5
mutex myMutex1;

myMutex1.lock();
// code 1
myMutex1.unlock();

myMutex1.lock();
// code 2
myMutex1.unlock();
// 错误代码  
// 根据 c++ 标准,在一个线程尝试对一个 mutex 对象进行 unlock 操作时,mutex 对象的所有权必须在这个线程上;也就是说,应该由同一个线程来对一个 mutex 对象进行 lock 和 unlock 操作,否则会产生未定义行为。题目中提到了 first, second, third 三个函数分别是由三个不同的线程来调用的,但我们是在 Foo 对象构造时(可以是在 create 这几个线程的主线程中,也可以是在三个线程中的任意一个)对两个 mutex 对象进行 lock 操作的,因此,调用 first 和 second 函数的两个线程中至少有一个在尝试获取其他线程所拥有的 mutex 对象的所有权。
// 一把锁的上锁与解锁在不同线程中进行,在windows上会报错,在Linux上不会报错,可能力扣用的跑代码的机器是Linux所以这样不会出现问题
class Foo {
mutex mtx1, mtx2;
public:
Foo() {
mtx1.lock(), mtx2.lock();
}

void first(function<void()> printFirst) {
printFirst();
mtx1.unlock();
}

void second(function<void()> printSecond) {
mtx1.lock();
printSecond();
mtx1.unlock();
mtx2.unlock();
}

void third(function<void()> printThird) {
mtx2.lock();
printThird();
mtx2.unlock();
}
};

// 我们只是通过 mutex 的机制来保护数据被同时访问,所以最好使用 lock_guard 或者 unique_lock 提供的 RAII 机制来管理 mutex 对象,而不是直接操作 mutex 对象;其中 lock_guard 只拥有构造和析构函数,用来实现 RAII 机制,而 unique_lock 是一个完整的 mutex 所有权包装器,封装了所有 mutex 的函数:

class Foo {
mutex mtx_1, mtx_2;
unique_lock<mutex> lock_1, lock_2;
public:
Foo() : lock_1(mtx_1, try_to_lock), lock_2(mtx_2, try_to_lock) {
}

void first(function<void()> printFirst) {
printFirst();
lock_1.unlock();
}

void second(function<void()> printSecond) {
lock_guard<mutex> guard(mtx_1);
printSecond();
lock_2.unlock();
}

void third(function<void()> printThird) {
lock_guard<mutex> guard(mtx_2);
printThird();
}
};

2、条件变量

class Foo {
condition_variable cv;
mutex mtx;
int k = 0;
public:
void first(function<void()> printFirst) {
printFirst();
k = 1;
cv.notify_all(); // 通知其他所有在等待唤醒队列中的线程
}

void second(function<void()> printSecond) {
unique_lock<mutex> lock(mtx); // lock mtx
cv.wait(lock, [this](){ return k == 1; }); // unlock mtx,并阻塞等待唤醒通知,需要满足 k == 1 才能继续运行
printSecond();
k = 2;
cv.notify_one(); // 随机通知一个(unspecified)在等待唤醒队列中的线程
}

void third(function<void()> printThird) {
unique_lock<mutex> lock(mtx); // lock mtx
cv.wait(lock, [this](){ return k == 2; }); // unlock mtx,并阻塞等待唤醒通知,需要满足 k == 2 才能继续运行
printThird();
}
};

https://yelinji.gitbooks.io/cpp_concurrency_in_action/content/ 作为对《C++ Concurrency in Action》的中文翻译。


1.4

清单 1.1 一个简单的Hello, Concurrent World程序:

#include <iostream>
#include <thread> //①
void hello() //②
{
std::cout << "Hello Concurrent World\n";
}
int main()
{
std::thread t(hello); //③
t.join(); //④
}

2.1线程管理的基础

std::thread可以用可调用类型构造,将带有函数调用符类型的实例传入std::thread类中,替换默认的构造函数。

class background_task
{
public:
void operator()() const
{
do_something();
do_something_else();
}
};

background_task f;
std::thread my_thread(f);