-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
更新视频代码以及 CMake 引入 fmt、Qt、Boost 的配置,增加使用 OpenMp 的选项
- Loading branch information
Showing
9 changed files
with
349 additions
and
11 deletions.
There are no files selected for viewing
40 changes: 40 additions & 0 deletions
40
code/ModernCpp-ConcurrentProgramming-Tutorial/35C++20信号量.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#include <iostream> | ||
#include <thread> | ||
#include <chrono> | ||
#include <random> | ||
#include <semaphore> | ||
using namespace std::chrono_literals; | ||
|
||
// 定义一个信号量,最大并发数为 3 | ||
std::counting_semaphore<3> semaphore{ 3 }; | ||
|
||
void handle_request(int request_id) { | ||
// 请求到达,尝试获取信号量 | ||
std::cout << "进入 handle_request 尝试获取信号量\n"; | ||
|
||
semaphore.acquire(); | ||
|
||
std::cout << "成功获取信号量\n"; | ||
|
||
// 此处延时三秒可以方便测试,会看到先输出 3 个“成功获取信号量”,因为只有三个线程能成功调用 acquire,剩余的会被阻塞 | ||
std::this_thread::sleep_for(3s); | ||
|
||
// 模拟处理时间 | ||
std::random_device rd; | ||
std::mt19937 gen{ rd() }; | ||
std::uniform_int_distribution<> dis(1, 5); | ||
int processing_time = dis(gen); | ||
std::this_thread::sleep_for(std::chrono::seconds(processing_time)); | ||
|
||
std::cout << std::format("请求 {} 已被处理\n", request_id); | ||
|
||
semaphore.release(); | ||
} | ||
|
||
int main() { | ||
// 模拟 10 个并发请求 | ||
std::vector<std::jthread> threads; | ||
for (int i = 0; i < 10; ++i) { | ||
threads.emplace_back(handle_request, i); | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
code/ModernCpp-ConcurrentProgramming-Tutorial/36C++20闩latch.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
#include <iostream> | ||
#include <thread> | ||
#include <chrono> | ||
#include <latch> | ||
using namespace std::chrono_literals; | ||
|
||
std::latch latch{ 10 }; | ||
|
||
void f(int id) { | ||
//todo.. 脑补任务 | ||
std::cout << std::format("线程 {} 执行完任务,开始等待其它线程执行到此处\n", id); | ||
latch.arrive_and_wait(); // 减少 并等待 count_down(1); wait(); 等待计数为 0 | ||
std::cout << std::format("线程 {} 彻底退出函数\n", id); | ||
} | ||
|
||
int main() { | ||
std::vector<std::jthread> threads; | ||
for (int i = 0; i < 10; ++i) { | ||
threads.emplace_back(f, i); | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
code/ModernCpp-ConcurrentProgramming-Tutorial/37C++20屏障barrier.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
#include <iostream> | ||
#include <omp.h> | ||
#include <string> | ||
#include <thread> | ||
|
||
void f(int start, int end, int thread_id) { | ||
for (int i = start; i <= end; ++i) { | ||
// 输出当前线程的数字 | ||
std::cout << std::to_string(i) + " "; | ||
|
||
// 等待所有线程同步到达 barrier 也就是等待都输出完数字 | ||
#pragma omp barrier | ||
|
||
// 每个线程输出完一句后,主线程输出轮次信息 | ||
#pragma omp master | ||
{ | ||
static int round_number = 1; | ||
std::cout << "\t第" << round_number++ << "轮结束\n"; | ||
std::this_thread::sleep_for(std::chrono::seconds(1)); | ||
} | ||
|
||
// 再次同步 等待所有线程(包括主线程)到达此处、避免其它线程继续执行打断主线程的输出 | ||
#pragma omp barrier | ||
} | ||
} | ||
|
||
int main() { | ||
constexpr int num_threads = 10; | ||
omp_set_num_threads(num_threads); | ||
|
||
#pragma omp parallel | ||
{ | ||
const int thread_id = omp_get_thread_num(); | ||
f(thread_id * 10 + 1, (thread_id + 1) * 10, thread_id); | ||
} | ||
|
||
} | ||
|
||
// https://godbolt.org/z/fabqhbx3P |
41 changes: 41 additions & 0 deletions
41
code/ModernCpp-ConcurrentProgramming-Tutorial/38第四章总结-勘误初始化顺序.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
#include <iostream> | ||
#include <thread> | ||
#include <vector> | ||
|
||
struct X { | ||
X() { | ||
// 假设 X 的初始化没那么快 | ||
std::this_thread::sleep_for(std::chrono::seconds(1)); | ||
std::puts("X"); | ||
v.resize(10, 6); | ||
} | ||
std::vector<int> v; | ||
}; | ||
|
||
struct Test { | ||
Test()/* : t{ &Test::f, this }*/ // 线程已经开始执行 | ||
{ | ||
// 严格意义来说 这里不算初始化 至少不算 C++ 标准的定义 | ||
} | ||
void start() | ||
{ | ||
t = std::thread{ &Test::f, this }; | ||
} | ||
~Test() { | ||
if (t.joinable()) | ||
t.join(); | ||
} | ||
void f()const { // 如果在函数执行的线程 f 中使用 x 则会存在问题。使用了未初始化的数据成员 ub | ||
std::cout << "f\n"; | ||
std::cout << x.v[9] << '\n'; | ||
} | ||
|
||
|
||
std::thread t; // 声明顺序决定了初始化顺序,优先初始化 t | ||
X x; | ||
}; | ||
|
||
int main() { | ||
Test t; | ||
t.start(); | ||
} |
49 changes: 49 additions & 0 deletions
49
code/ModernCpp-ConcurrentProgramming-Tutorial/39原子类型atomic.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
#include <iostream> | ||
#include <thread> | ||
#include <chrono> | ||
#include <atomic> | ||
using namespace std::chrono_literals; | ||
|
||
// 不要这样使用 不要在多线程并发中使用 volatile | ||
// 它的行为是不保证的 | ||
std::atomic<int> n = 0; | ||
|
||
void read(){ | ||
while(true){ | ||
std::this_thread::sleep_for(500ms); | ||
std::cout << n.load() << '\n'; | ||
} | ||
} | ||
|
||
void write(){ | ||
while (true){ | ||
++n; | ||
} | ||
} | ||
|
||
// 数据竞争 数据竞争未定义行为 | ||
// 优化会假设你的程序中没有未定义行为 | ||
|
||
// C 语言的平凡的结构体 | ||
struct trivial_type { | ||
int x; | ||
float y; | ||
}; | ||
|
||
int main(){ | ||
// 创建一个 std::atomic<trivial_type> 对象 | ||
std::atomic<trivial_type> atomic_my_type{ { 10, 20.5f } }; | ||
|
||
// 使用 store 和 load 操作来设置和获取值 | ||
trivial_type new_value{ 30, 40.5f }; | ||
atomic_my_type.store(new_value); | ||
|
||
std::cout << "x: " << atomic_my_type.load().x << ", y: " << atomic_my_type.load().y << std::endl; | ||
|
||
// 使用 exchange 操作 | ||
trivial_type exchanged_value = atomic_my_type.exchange({ 50, 60.5f }); | ||
std::cout << "交换前的 x: " << exchanged_value.x | ||
<< ", 交换前的 y: " << exchanged_value.y << std::endl; | ||
std::cout << "交换后的 x: " << atomic_my_type.load().x | ||
<< ", 交换后的 y: " << atomic_my_type.load().y << std::endl; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
#include <QCoreApplication> | ||
#include <QThreadPool> | ||
#include <QRunnable> | ||
#include <QDebug> | ||
|
||
int main(int argc, char* argv[]) { | ||
QCoreApplication app(argc, argv); | ||
|
||
QThreadPool* threadPool = QThreadPool::globalInstance(); | ||
|
||
// 线程池最大线程数 | ||
qDebug() << threadPool->maxThreadCount(); | ||
|
||
for (int i = 0; i < 20; ++i) { | ||
threadPool->start([i]{ | ||
qDebug() << QString("thread id %1").arg(i); | ||
}); | ||
} | ||
// 当前活跃线程数 10 | ||
qDebug() << threadPool->activeThreadCount(); | ||
|
||
app.exec(); | ||
} |
118 changes: 118 additions & 0 deletions
118
code/ModernCpp-ConcurrentProgramming-Tutorial/41实现一个线程池.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
#include <iostream> | ||
#include <mutex> | ||
#include <condition_variable> | ||
#include <atomic> | ||
#include <queue> | ||
#include <functional> | ||
#include <thread> | ||
#include <vector> | ||
#include <future> | ||
#include <memory> | ||
#include <syncstream> | ||
using namespace std::chrono_literals; | ||
|
||
inline std::size_t default_thread_pool_size() noexcept{ | ||
std::size_t num_threads = std::thread::hardware_concurrency(); | ||
num_threads = num_threads == 0 ? 2 : num_threads; // 防止无法检测当前硬件,让我们线程池至少有 2 个线程 | ||
return num_threads; | ||
} | ||
|
||
class ThreadPool{ | ||
public: | ||
using Task = std::packaged_task<void()>; | ||
|
||
ThreadPool(const ThreadPool&) = delete; | ||
ThreadPool& operator=(const ThreadPool&) = delete; | ||
|
||
ThreadPool(std::size_t num_thread = default_thread_pool_size()) : | ||
stop_{ false }, num_thread_{ num_thread } | ||
{ | ||
start(); | ||
} | ||
~ThreadPool(){ | ||
stop(); | ||
} | ||
|
||
void stop(){ | ||
stop_ = true; | ||
cv_.notify_all(); | ||
for (auto& thread : pool_){ | ||
if (thread.joinable()) | ||
thread.join(); | ||
} | ||
pool_.clear(); | ||
} | ||
|
||
template<typename F, typename ...Args> | ||
std::future<std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>> submit(F&& f, Args&&...args){ | ||
using RetType = std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>; | ||
if(stop_){ | ||
throw std::runtime_error("ThreadPool is stopped"); | ||
} | ||
auto task = std::make_shared<std::packaged_task<RetType()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...)); | ||
|
||
std::future<RetType> ret = task->get_future(); | ||
|
||
{ | ||
std::lock_guard<std::mutex> lc{ mutex_ }; | ||
tasks_.emplace([task] {(*task)(); }); | ||
} | ||
cv_.notify_one(); | ||
|
||
return ret; | ||
} | ||
|
||
void start(){ | ||
for (std::size_t i = 0; i < num_thread_; ++i){ | ||
pool_.emplace_back([this]{ | ||
while (!stop_) { | ||
Task task; | ||
{ | ||
std::unique_lock<std::mutex> lock{ mutex_ }; | ||
cv_.wait(lock, [this] {return stop_ || !tasks_.empty(); }); | ||
if (tasks_.empty()) return; | ||
task = std::move(tasks_.front()); | ||
tasks_.pop(); | ||
} | ||
task(); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
private: | ||
std::mutex mutex_; | ||
std::condition_variable cv_; | ||
std::atomic<bool> stop_; | ||
std::atomic<std::size_t> num_thread_; | ||
std::queue<Task> tasks_; | ||
std::vector<std::thread> pool_; | ||
}; | ||
|
||
int print_task(int n) { | ||
std::osyncstream{ std::cout } << "Task " << n << " is running on thr: " << | ||
std::this_thread::get_id() << '\n'; | ||
return n; | ||
} | ||
int print_task2(int n) { | ||
std::osyncstream{ std::cout } << "🐢🐢🐢 " << n << " 🐉🐉🐉" << std::endl; | ||
return n; | ||
} | ||
|
||
struct X { | ||
void f(const int& n) const { | ||
std::osyncstream{ std::cout } << &n << '\n'; | ||
} | ||
}; | ||
|
||
int main() { | ||
ThreadPool pool{ 4 }; // 创建一个有 4 个线程的线程池 | ||
|
||
X x; | ||
int n = 6; | ||
std::cout << &n << '\n'; | ||
auto t = pool.submit(&X::f, &x, n); // 默认复制,地址不同 | ||
auto t2 = pool.submit(&X::f, &x, std::ref(n)); | ||
t.wait(); | ||
t2.wait(); | ||
} // 析构自动 stop()自动 stop() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters