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 f3a91a6

Browse files
committed
修改第四章的"异步任务执行"措辞以及分段
1 parent 943b019 commit f3a91a6

File tree

1 file changed

+22
-9
lines changed

1 file changed

+22
-9
lines changed

‎md/04同步操作.md‎

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -972,9 +972,11 @@ bool wait_loop(){
972972
973973
## 异步任务执行
974974
975-
假设有一系列耗时任务需要完成,我们会使用异步多线程执行这些任务,从而减轻主线程的计算压力。比如:一些带 UI 程序,我们不能阻塞 UI 线程(不然可能造成界面卡顿),耗时任务应该是异步创建线程执行的
975+
在开发带有 UI 的程序时,主线程用于处理 UI 更新和用户交互,如果在主线程中执行耗时任务会导致界面卡顿。因此,需要使用异步任务来减轻主线程的压力。以下是一个使用 Qt 实现异步任务的示例,展示了如何在不阻塞 UI 线程的情况下执行耗时任务,并更新进度条
976976
977-
比如,一个**进度条**,我们以 Qt 为例:
977+
### 背景介绍
978+
979+
在 Qt 中,GUI 控件通常只能在创建它们的线程中进行操作,因为它们是线程不安全的。我们可以使用 `QMetaObject::invokeMethod` 来跨线程调用主线程上的控件方法,从而在其他线程中安全地更新 UI 控件。以下代码示例展示了如何通过 `QMetaObject::invokeMethod` 确保 UI 控件的更新操作在主线程中执行。
978980
979981
```cpp
980982
void task(){
@@ -998,15 +1000,17 @@ void task(){
9981000
}
9991001
```
10001002

1001-
> Qt 中,GUI 控件通常只能在创建它们的线程中进行操作,是因为 GUI 控件是线程不安全的。而 `QMetaObject::invokeMethod` 函数是 Qt 中的一种跨线程通信机制,它允许我们在不同的线程之间安全地发送信号和调用槽函数。也就是说我们传递的 lambda 是在**主线程运行的**。
1002-
1003-
很显然,我们创建了一个异步任务,指明执行策略,它在线程中执行。如果不这样做,将会**卡界面**(你可以把这个函数的第一行与最后一行注释掉)。
1003+
上面的代码创建了一个异步任务,并指明了执行策略。任务在线程中执行,不会阻塞 UI 线程。如果不这样做,界面将会卡顿(可以尝试将函数的第一行与最后一行注释掉以验证这一点)。
10041004

10051005
![进度条](../image/第四章/进度条.png)
10061006

1007-
在启动进度条后,能够正常点击"**测试**"按钮然后触发弹窗,就是没有问题,你也可以尝试不创建线程,那么界面将会卡主,你点击不了"**测试**"按钮,也移动不了这个窗口。
1007+
在启动进度条后,能够正常点击"**测试**"按钮并触发弹窗,说明 UI 没有被阻塞。相反,如果不使用线程,界面将会卡住,无法点击"**测试**"按钮或移动窗口。
1008+
1009+
### 项目说明
1010+
1011+
项目使用 Visual Studio 编写,可以直接安装 Qt 插件后打开。项目结构简单,所有界面与设置均通过代码控制,无需进行其他 UI 操作。重点关注 `async_progress_bar.h``async_progress_bar.cpp``main.cpp` 这三个文件,它们位于仓库的 **`code`** 文件夹中。
10081012

1009-
我们的项目是使用 visual studio 编写的,你可以直接安装 Qt 插件后使用它打开。此项目很小,为了简洁,所有的的界面与设置均是代码控制,而没有进行别的 ui 操作,也就是说,你只需要注意 `async_progress_bar.h`、`async_progress_bar.cpp`、`main.cpp` 这三个文件即可。它们存放在仓库以及目录 **`code`** 文件夹中。
1013+
### 完整代码实现
10101014

10111015
```cpp
10121016
class async_progress_bar : public QMainWindow{
@@ -1099,8 +1103,17 @@ async_progress_bar::async_progress_bar(QWidget *parent)
10991103
}
11001104
```
11011105
1102-
为了展示 `QMetaObject::invokeMethod` 中的 lambda 是在主线程运行,我们打印了线程 ID,因为 C++11 的 `std::this_thread::get_id()` 返回的那个内部类型没办法直接转换为 `unsigned int`,我们就直接使用了 win32 的 API *`_Thrd_id()`* 了。如果您是 Linux 之类的环境,使用 [POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/) 接口 [*`pthread_self()`*](https://pubs.opengroup.org/onlinepubs/009696699/functions/pthread_self.html)。
1106+
### 注意事项
1107+
1108+
- `QMetaObject::invokeMethod` 的 lambda 是在主线程运行的,通过显示的线程 ID 可以验证这一点。
1109+
- 使用 `std::async` 的 `std::launch::async` 参数强制异步执行任务,以确保任务在新线程中运行。
1110+
1111+
### 跨平台兼容性
1112+
1113+
C++11 的 `std::this_thread::get_id()` 返回的内部类型没办法直接转换为 `unsigned int`,我们就直接使用了 win32 的 API *`_Thrd_id()`* 了。如果您是 Linux 之类的环境,使用 [POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/) 接口 [*`pthread_self()`*](https://pubs.opengroup.org/onlinepubs/009696699/functions/pthread_self.html)。
1114+
1115+
### 实践建议
11031116
11041117
这个例子其实很好的展示了多线程异步的作用,因为有 UI,所以很直观,毕竟如果你不用线程,那么不就卡界面了,用了就没事。
11051118
1106-
建议打开此项目自己编译运行,并尝试修改,如有必要,建议自己也写一遍,代码较为简单,就不再介绍了
1119+
建议下载并运行此项目,通过实际操作理解代码效果。同时,可以尝试修改代码,观察不同情况下 UI 的响应情况,以加深对异步任务处理的理解

0 commit comments

Comments
(0)

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