Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit de1f91a

Browse files
committed
更新视频代码,修改第四章措辞、格式、以及补充代码示例
1 parent 8954e3c commit de1f91a

File tree

8 files changed

+222
-11
lines changed

8 files changed

+222
-11
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <iostream>
2+
#include <thread>
3+
#include <future> // 引入 future 头文件
4+
5+
void f() {
6+
std::cout << std::this_thread::get_id() << '\n';
7+
}
8+
9+
int main() {
10+
auto t = std::async([] {});
11+
std::future<void> future{ std::move(t) };
12+
future.wait(); // Error! 抛出异常
13+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#include <iostream>
2+
#include <thread>
3+
#include <future>
4+
5+
template<typename R, typename...Ts, typename...Args>
6+
void async_task(std::packaged_task<R(Ts...)>& task, Args&&...args) {
7+
// todo..
8+
task(std::forward<Args>(args)...);
9+
}
10+
11+
int main() {
12+
std::packaged_task<int(int, int)> task([](int a, int b) {
13+
return a + b;
14+
});
15+
16+
int value = 50;
17+
18+
std::future<int> future = task.get_future();
19+
20+
// 创建一个线程来执行异步任务
21+
std::thread t{ [&] { async_task(task, value, value); } };
22+
std::cout << future.get() << '\n';
23+
t.join();
24+
}
25+
26+
//int main(){
27+
// std::cout << "main: " << std::this_thread::get_id() << '\n';
28+
//
29+
// // 只能移动不能复制
30+
// std::packaged_task<double(int, int)> task{ [](int a, int b) {
31+
// std::cout << "packaged_task: " << std::this_thread::get_id() << '\n';
32+
// return std::pow(a, b);
33+
// } };
34+
//
35+
// std::future<double> future = task.get_future();
36+
//
37+
// // task(10, 2); // 调用 此处执行任务
38+
//
39+
// std::thread t{ std::move(task) ,10,2 };
40+
//
41+
// std::cout << "------\n";
42+
//
43+
// std::cout << future.get() << '\n'; // 会阻塞,直到任务执行完毕
44+
//
45+
// t.join();
46+
//}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#include <iostream>
2+
#include <thread>
3+
#include <future>
4+
#include <chrono>
5+
using namespace std::chrono_literals;
6+
7+
void f(std::promise<int> obj ,int num){
8+
// todo..
9+
obj.set_value(num * num); // 调用了 set_value
10+
// todo..
11+
std::this_thread::sleep_for(5s); // 模拟一些计算
12+
}
13+
14+
void throw_function(std::promise<int> prom) {
15+
prom.set_value(100);
16+
try {
17+
// todo..
18+
throw std::runtime_error("一个异常");
19+
}
20+
catch (...) {
21+
try {
22+
// 共享状态的 promise 已存储值,调用 set_exception 产生异常
23+
prom.set_exception(std::current_exception());
24+
}
25+
catch (std::exception& e) {
26+
std::cerr << "来自 set_exception 的异常: " << e.what() << '\n';
27+
}
28+
}
29+
}
30+
31+
int main() {
32+
std::promise<int> prom;
33+
std::future<int> fut = prom.get_future();
34+
35+
std::thread t(throw_function, std::move(prom));
36+
37+
std::cout << "等待线程执行,抛出异常并设置\n";
38+
std::cout << "值:" << fut.get() << '\n'; // 100
39+
40+
t.join();
41+
}
42+
43+
44+
//int main(){
45+
// std::promise<int> promise;
46+
//
47+
// auto future = promise.get_future(); // 关联了
48+
//
49+
// std::thread t{ f,std::move(promise), 10 };
50+
// // f(std::move(promise), 10);
51+
//
52+
// std::cout << future.get() << '\n'; // 阻塞,直至结果可用
53+
// std::cout << "end\n";
54+
// t.join();
55+
//}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <iostream>
2+
#include <thread>
3+
#include <future>
4+
5+
int main(){
6+
std::future<void>future = std::async([] {});
7+
std::cout << std::boolalpha << future.valid() << '\n'; // true
8+
future.get();
9+
std::cout << std::boolalpha << future.valid() << '\n'; // false
10+
try {
11+
future.get(); // 抛出 future_errc::no_state 异常
12+
}
13+
catch (std::exception& e) {
14+
std::cerr << e.what() << '\n';
15+
}
16+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include <iostream>
2+
#include <thread>
3+
#include <future>
4+
5+
std::string fetch_data() {
6+
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟耗时操作
7+
return "从网络获取的数据!";
8+
}
9+
10+
int main() {
11+
std::future<std::string> future_data = std::async(std::launch::async, fetch_data);
12+
13+
// // 转移共享状态,原来的 future 被清空 valid() == false
14+
std::shared_future<std::string> shared_future_data = future_data.share();
15+
16+
// 多个线程持有一个 shared_future 对象并操作
17+
18+
// 第一个线程等待结果并访问数据
19+
std::thread thread1([shared_future_data] {
20+
std::cout << "线程1:等待数据中..." << std::endl;
21+
shared_future_data.wait(); // 等待结果可用
22+
std::cout << "线程1:收到数据:" << shared_future_data.get() << std::endl;
23+
});
24+
25+
// 第二个线程等待结果并访问数据
26+
std::thread thread2([shared_future_data] {
27+
std::cout << "线程2:等待数据中..." << std::endl;
28+
shared_future_data.wait();
29+
std::cout << "线程2:收到数据:" << shared_future_data.get() << std::endl;
30+
});
31+
32+
thread1.join();
33+
thread2.join();
34+
35+
std::promise<std::string> p;
36+
std::shared_future<std::string> sf{ p.get_future() }; // 隐式转移所有权
37+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include <chrono>
2+
#include <iostream>
3+
#include <iomanip>
4+
using namespace std::chrono_literals;
5+
6+
int main(){
7+
auto now = std::chrono::system_clock::now();
8+
time_t now_time = std::chrono::system_clock::to_time_t(now);
9+
std::cout << "Current time:\t" << std::put_time(std::localtime(&now_time), "%H:%M:%S\n");
10+
11+
auto now2 = std::chrono::steady_clock::now();
12+
now_time = std::chrono::system_clock::to_time_t(now);
13+
std::cout << "Current time:\t" << std::put_time(std::localtime(&now_time), "%H:%M:%S\n");
14+
}

‎code/ModernCpp-ConcurrentProgramming-Tutorial/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "C
1212
add_compile_options("-finput-charset=UTF-8" "-fexec-charset=UTF-8")
1313
endif()
1414

15-
add_executable(${PROJECT_NAME} "26使用条件变量实现后台提示音播放.cpp")
15+
add_executable(${PROJECT_NAME} "32限时等待-时钟.cpp")
1616

1717

1818
# 设置 SFML 的 CMake 路径

‎md/04同步操作.md

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ auto sum(ForwardIt first, ForwardIt last) {
722722
723723
> [运行](https://godbolt.org/z/r19MYcv6e)测试。
724724
725-
相比于之前,其实不同无非是定义了 `std::vector<std::packaged_task<value_type()>> tasks` 与 `std::vector<std::future<value_type>> futures` ,然后在循环中制造任务插入容器,关联 tuple,再放到线程中执行。最后汇总的时候写一个循环,`futures[i].get()` 获取任务的返回值加起来即可。
725+
相比于之前,其实不同无非是定义了 `std::vector<std::packaged_task<value_type()>> tasks` 与 `std::vector<std::future<value_type>> futures` ,然后在循环中制造任务插入容器,关联 future,再放到线程中执行。最后汇总的时候写一个循环,`futures[i].get()` 获取任务的返回值加起来即可。
726726
727727
到此,也就可以了。
728728
@@ -799,7 +799,7 @@ int main() {
799799
来自线程的异常: 一个异常
800800
```
801801
802-
你可能对这段代码还有一些疑问:我们写的是 `promised<int>` ,但是却没有使用 `set_value` 设置值,你可能会想着再写一行 `prom.set_value(0)`?
802+
你可能对这段代码还有一些疑问:我们写的是 `promise<int>` ,但是却没有使用 `set_value` 设置值,你可能会想着再写一行 `prom.set_value(0)`?
803803
804804
共享状态的 promise 已经存储值或者异常,再次调用 `set_value`(`set_exception`) 会抛出 [std::future_error](https://zh.cppreference.com/w/cpp/thread/future_error) 异常,将错误码设置为 [`promise_already_satisfied`](https://zh.cppreference.com/w/cpp/thread/future_errc)。这是因为 `std::promise` 对象只能是存储值或者异常其中一种,而**无法共存**
805805
@@ -893,13 +893,43 @@ _Ty& get() {
893893
894894
如果需要进行多次 `get` 调用,可以考虑使用下文提到的 `std::shared_future`
895895
896-
### 多个线程的等待
896+
### 多个线程的等待`std::shared_future`
897897
898-
之前的例子中都在用 `std::future` ,不过 `std::future` 也有局限性。很多线程在等待的时候,只有一个线程能获取结果。当多个线程等待相同事件的结果时,就需要使用 `std::shared_future` 来替代 `std::future` 了。`std::future``std::shared_future` 的区别就如同 `std::unique_ptr``std::shared_ptr` 一样。
898+
之前的例子中我们一直使用 `std::future`,但 `std::future` 有一个局限:**future 是一次性的**,它的结果只能被一个线程获取。`get()` 成员函数只能调用一次,当结果被某个线程获取后,`std::future` 就无法再用于其他线程。
899+
900+
```cpp
901+
int task(){
902+
// todo..
903+
return 10;
904+
}
905+
906+
void thread_functio(std::future<int>& fut){
907+
// todo..
908+
int result = fut.get();
909+
std::cout << result << '\n';
910+
// todo..
911+
}
912+
913+
int main(){
914+
auto future = std::async(task); // 启动耗时的异步任务
915+
916+
// 可能有多个线程都需要此任务的返回值,于是我们将与其关联的 future 对象的引入传入
917+
std::thread t{ thread_functio,std::ref(future) };
918+
std::thread t2{ thread_functio,std::ref(future) };
919+
t.join();
920+
t2.join();
921+
}
922+
```
923+
924+
> 可能有多个线程都需要耗时的异步任务的返回值,于是我们将与其关联的 future 对象的引入传给线程对象,让它能在需要的时候获取。
925+
>
926+
> 但是这存在个问题,future 是一次性的,只能被调用一次 `get()` 成员函数,所以以上代码存在问题。
927+
928+
此时就需要使用 `std::shared_future` 来替代 `std::future` 了。`std::future` 与 `std::shared_future` 的区别就如同 `std::unique_ptr`、`std::shared_ptr` 一样。
899929
900930
`std::future` 是只能移动的,其所有权可以在不同的对象中互相传递,但只有一个对象可以获得特定的同步结果。而 `std::shared_future` 是可复制的,多个对象可以指代同一个共享状态。
901931
902-
在多个线程中对**同一个 **`std::shared_future` 对象进行操作时(如果没有进行同步保护)存在竞争条件。而从多个线程访问同一共享状态,若每个线程都是通过其自身的 `shared_future` 对象**副本**进行访问,则是安全的。
932+
在多个线程中对**同一个 **`std::shared_future` 对象进行操作时(如果没有进行同步保护)存在条件竞争。而从多个线程访问同一共享状态,若每个线程都是通过其自身的 `shared_future` 对象**副本**进行访问,则是安全的。
903933
904934
```cpp
905935
std::string fetch_data() {
@@ -932,7 +962,7 @@ int main() {
932962
}
933963
```
934964
935-
这段代码存在数据竞争,就如同我们先前所说:"***在多个线程中对**同一个 **`std::shared_future` 对象进行操作时(如果没有进行同步保护)存在竞争条件***",它并没有提供线程安全的方式。而我们的 lambda 是按引用传递,也就是"**同一个**"进行操作了。可以改为:
965+
这段代码存在数据竞争,就如同我们先前所说:"***在多个线程中对**同一个 **`std::shared_future` 对象进行操作时(如果没有进行同步保护)存在条件竞争***",它并没有提供线程安全的方式。而我们的 lambda 是按引用传递,也就是"**同一个**"进行操作了。可以改为:
936966
937967
```cpp
938968
std::string fetch_data() {
@@ -962,13 +992,13 @@ int main() {
962992
}
963993
```
964994
965-
这样访问的就都是 `std::shared_future` 的副本了,我们的 lambda 按复制捕获 std::shared_future 对象,每个线程都有一个 shared_future 的副本,这样不会有任何问题。这一点和 `std::shared_ptr` 类似[^2]
995+
这样访问的就都是 `std::shared_future` 的副本了,我们的 lambda 按复制捕获 `std::shared_future` 对象,每个线程都有一个 shared_future 的副本,这样不会有任何问题。这一点和 `std::shared_ptr` 类似[^2]
966996
967997
`std::promise` 也同,它的 `get_future()` 成员函数一样可以用来构造 `std::shared_future`,虽然它的返回类型是 `std::future`,不过不影响,这是因为 `std::shared_future` 有一个 `std::future<T>&&` 参数的[构造函数](https://zh.cppreference.com/w/cpp/thread/shared_future/shared_future),转移 `std::future` 的所有权。
968998
969999
```cpp
970-
std::promise<std::string>p;
971-
std::shared_future<std::string>sf{ p.get_future() }; // 隐式转移所有权
1000+
std::promise<std::string>p;
1001+
std::shared_future<std::string>sf{ p.get_future() }; // 隐式转移所有权
9721002
```
9731003
9741004
就不需要再强调了。
@@ -1010,7 +1040,7 @@ class duration;
10101040
如你所见,它默认的时钟节拍是 1,这是一个很重要的类,标准库通过它定义了很多的时间类型,比如 **`std::chrono::minutes`** 是分钟类型,那么它的 `Period` 就是 `std::ratio<60>` ,因为一分钟等于 60 秒。
10111041
10121042
```cpp
1013-
std::chrono::minutes std::chrono::duration</* int29 */, std::ratio<60>>
1043+
usingminutes = duration<int, ratio<60>>;
10141044
```
10151045
10161046
稳定时钟(Steady Clock)是指提供稳定、持续递增的时间流逝信息的时钟。它的特点是不受系统时间调整或变化的影响,即使在系统休眠或时钟调整的情况下,它也能保持稳定。在 C++ 标准库中,[`std::chrono::steady_clock`](https://zh.cppreference.com/w/cpp/chrono/steady_clock) 就是一个稳定时钟。它通常用于测量时间间隔和性能计时等需要高精度和稳定性的场景。可以通过 `is_steady` 静态常量判断当前时钟是否是稳定时钟。

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /