1.并行、互斥量、临界区
#include <iostream>
#include <thread>
#include <mutex>
int v = 1;
void critical_section(int change_v)
{
static std::mutex mtx;
//std::lock_guard<std::mutex> lock(mtx);
std::unique_lock<std::mutex> lock(mtx);
//执行竞争
v = change_v;
std::cout << v << std::endl;
lock.unlock();
//在此期间,任何人都可以争抢V的持有权
//再次加锁
lock.lock();
v += 1;
std::cout << v << std::endl;
}
int main()
{
//1.创建线程实例
std::thread t([]() {
std::cout << "hello word." << std::endl;
});
t.join();
//2.互斥量与临界区
std::thread t1(critical_section, 2), t2(critical_section, 3);
t1.join();
t2.join();
std::cout << v << std::endl;
return 0;
}
2.期物
#include <iostream>
#include <future>
#include <thread>
int main()
{
std::packaged_task<int()> task([]() {return 7; });
std::future<int> result = task.get_future();
std::thread(std::move(task)).detach();
std::cout << "waiting...";
result.wait();
std::cout << "done" << std::endl << "future result is" << result.get() << std::endl;
return 0;
}
3.条件变量
#include <queue>
#include <chrono>
#include <mutex>
#include <thread>
#include <iostream>
#include <condition_variable>
int main()
{
std::queue<int> produced_nums;
std::mutex mtx;
std::condition_variable cv;
bool notified = false; //通知信号
//生产者
auto producer = [&]() {
for (int i = 0; ; i++)
{
std::this_thread::sleep_for(std::chrono::milliseconds(900));
std::unique_lock<std::mutex> lock(mtx);
std::cout << "producing" << i << std::endl;
produced_nums.push(i);
notified = true;
cv.notify_all();
}
};
//消费者
auto consumer = [&]() {
while (true)
{
std::unique_lock<std::mutex> lock(mtx);
while (!notified)
{
cv.wait(lock);
}
//短暂取消锁
lock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
lock.lock();
while (!produced_nums.empty())
{
std::cout << "consuming" << produced_nums.front() << std::endl;
produced_nums.pop();
}
notified = false;
}
};
//在不同线程中运行
std::thread p(producer);
std::thread cs[2];
for (int i = 0; i < 2; ++i)
{
cs[i] = std::thread(consumer);
}
p.join();
for (int i = 0; i < 2; ++i)
{
cs[i].join();
}
return 0;
}
4.原子操作
#include <atomic>
#include <thread>
#include <iostream>
std::atomic<int> count = { 0 };
struct A
{
float x;
int y;
long long z;
};
int main()
{
//原子操作
std::thread t1([]() {
count.fetch_add(1);
});
std::thread t2([]() {
count++;
count += 1;
});
t1.join();
t2.join();
std::cout << count << std::endl;
//判读类型是否满足该架构对内存对齐的要求
std::atomic<A> a;
std::cout << std::boolalpha << a.is_lock_free() << std::endl;
return 0;
}
5.内存顺序
1)宽松模型
在此模型下,单个线程内的原子操作都是顺序执行的,不允许指令重排,但不同线程间原子操作的顺序是任意的。类型通过 std::memory_order_relaxed 指定。
#include <atomic>
#include <thread>
#include <iostream>
#include <vector>
int main()
{
//宽松模型
std::cout << "宽松模型" << std::endl;
std::atomic<int> counter = { 0 };
std::vector<std::thread> vt;
for (int i = 0; i < 100; ++i)
{
vt.emplace_back([&]() {
counter.fetch_add(1, std::memory_order_relaxed);
});
}
for (auto& t : vt)
{
t.join();
}
std::cout << "finnal counter:" << counter << std::endl;
return 0;
}
2)释放/消费模型:在此模型中,我们开始限制进程间的操作顺序,如果某个线程需要修改某个值,但另一个线程会对该值的某次操作产生依赖,即后者依赖前者。具体而言,线程 A 完成了三次对 x 的写操作,线程 B 仅依赖其中第三次 x 的写操作,与 x 的前两次写行为无关,则当 A 主动 x.release() 时候(即使用 std::memory_order_release),选项 std::memory_order_consume 能够确保 B 在调用 x.load() 时候观察到 A 中第三次对 x 的写操作。
#include <atomic>
#include <thread>
#include <iostream>
#include <vector>
int main()
{
std::atomic<int*> ptr(nullptr);
int v;
std::thread producer([&]() {
int *p = new int(42);
v = 1024;
ptr.store(p, std::memory_order_release);
});
std::thread consumer([&]() {
int *p;
while (!(p = ptr.load(std::memory_order_consume)));
std::cout << "p:" << *p << std::endl;
std::cout << "v:" << v << std::endl;
});
producer.join();
consumer.join();
return 0;
}
3)释放/获取模型:在此模型下,我们可以进一步加紧对不同线程间原子操作的顺序的限制,在释放 std::memory_order_release 和获取 std::memory_order_acquire 之间规定时序,即发生在释放(release)操作之前的所有写操作,对其他线程的任何获取(acquire)操作都是可见的,亦即发生顺序(happens-before)。
可以看到,std::memory_order_release 确保了它之前的写操作不会发生在释放操作之后,是一个向后的屏障(backward),而 std::memory_order_acquire 确保了它之前的写行为不会发生在该获取操作之后,是一个向前的屏障(forward)。对于选项 std::memory_order_acq_rel 而言,则结合了这两者的特点,唯一确定了一个内存屏障,使得当前线程对内存的读写不会被重排并越过此操作的前后
#include <atomic>
#include <thread>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v;
std::atomic<int> flag = { 0 };
std::thread release([&]() {
v.push_back(42);
flag.store(1, std::memory_order_release);
});
std::thread acqrel([&]() {
int expected = 1;
while (!flag.compare_exchange_strong(expected, 2, std::memory_order_acq_rel)) {
expected = 1;
}
});
std::thread acquire([&]() {
while (flag.load(std::memory_order_acquire) < 2);
std::cout << v.at(0) << std::endl;
});
release.join();
acqrel.join();
acquire.join();
return 0;
}
4)顺序一致模型:在此模型下,原子操作满足顺序一致性,进而可能对性能产生损耗。可显式的通过 std::memory_order_seq_cst 进行指定。
#include <atomic>
#include <thread>
#include <iostream>
#include <vector>
int main()
{
std::atomic<int> counter = { 0 };
std::vector<std::thread> vt;
for (int i = 0; i < 100; ++i) {
vt.emplace_back([&]() {
std::cout << "current counter:" << counter << std::endl;
counter.fetch_add(1, std::memory_order_seq_cst);
});
}
for (auto & t : vt)
{
t.join();
}
std::cout << "final counter:" << counter << std::endl;
return 0;
}