2323< meta property ="og:site_name " content ="Acl developer ">
2424< meta property ="og:description " content ="一、概述人们在谈论协程编程时,往往与编写命令行网络程序有关,如编写网络客户端与网络服务器程序,很少涉及到客户端 UI 相关的界面编程。Acl 协程库是支持在 Windows 下的 UI 界面编程的,因为 Acl 协程的事件引擎支持了界面消息传递过程。最近学习了一下 QT UI 编程,轻松将 Acl 协程与 QT UI 集成在一起,从而实现了 QT 界面协程化,使开发人员在使用 QT 编写界面程序时 ">
2525< meta property ="og:locale " content ="zh_CN ">
26+ < meta property ="og:image " content ="https://acl-dev.cn/img/fiber_qt.jpg ">
2627< meta property ="article:published_time " content ="2024-09-24T13:01:24.000Z ">
27- < meta property ="article:modified_time " content ="2024-09-25T15:44:13.674Z ">
28+ < meta property ="article:modified_time " content ="2025-03-31T13:28:23.355Z ">
2829< meta property ="article:author " content ="zsxxsz ">
2930< meta property ="article:tag " content ="协程编程 ">
3031< meta name ="twitter:card " content ="summary_large_image ">
32+ < meta name ="twitter:image " content ="https://acl-dev.cn/img/fiber_qt.jpg ">
3133
3234
3335
237239 < span class ="post-meta mr-2 ">
238240 < i class ="iconfont icon-chart "> </ i >
239241
240- 3 .7k 字
242+ 4 .7k 字
241243
242244 </ span >
243245
248250
249251
250252
251- 32 分钟
253+ 40 分钟
252254
253255 </ span >
254256
@@ -291,7 +293,7 @@ <h1 style="display: none">在 QT 界面编程中使用协程</h1>
291293
292294 < div class ="markdown-body ">
293295
294- < h1 id ="一、概述 "> < a href ="#一、概述 " class ="headerlink " title ="一、概述 "> </ a > 一、概述</ h1 > < p > 人们在谈论协程编程时,往往与编写命令行网络程序有关,如编写网络客户端与网络服务器程序,很少涉及到客户端 UI 相关的界面编程。Acl 协程库是支持在 Windows 下的 UI 界面编程的,因为 Acl 协程的事件引擎支持了界面消息传递过程。最近学习了一下 QT UI 编程,轻松将 Acl 协程与 QT UI 集成在一起,从而实现了 QT 界面协程化,使开发人员在使用 QT 编写界面程序时,编写网络模块变得非常简单。</ p >
296+ < h1 id ="一、概述 "> < a href ="#一、概述 " class ="headerlink " title ="一、概述 "> </ a > 一、概述</ h1 > < p > 人们在谈论协程编程时,往往与编写命令行网络程序有关,如编写网络客户端与网络服务器程序,很少涉及到客户端 UI 相关的界面编程。Acl 协程库是支持在 Windows 下的 UI 界面编程的,因为 < a target =" _blank " rel =" noopener " href =" https://github.com/acl-dev/acl/ " > Acl</ a > 协程的事件引擎支持了界面消息传递过程。最近学习了一下 QT UI 编程,轻松将 Acl 协程与 QT UI 集成在一起,从而实现了 QT 界面协程化,使开发人员在使用 QT 编写界面程序时,编写网络模块变得非常简单。</ p >
295297< p > 本文结合 Acl 中 lib_fiber/samples-gui/QtFiber 示例,演示了如何将 Acl 协程功能集成到 QT 界面中,实现了网络模块与界面模块的融合。</ p >
296298< h1 id ="二、集成 "> < a href ="#二、集成 " class ="headerlink " title ="二、集成 "> </ a > 二、集成</ h1 > < h2 id ="2-1、编译-Acl "> < a href ="#2-1、编译-Acl " class ="headerlink " title ="2.1、编译 Acl "> </ a > 2.1、编译 Acl</ h2 > < p > 目前 QT IDE 还无法直接使用 Acl 里的 CMakeLists.txt 文件编译 ACL,可以借助于 VC2019 打开 Acl 里的 acl_cpp_vc2019.sln 工程编译 Acl 五个库的动态库,分别为:lib_acl.dll, lib_protocol.dll, lib_acl_cpp.dll, libfiber.dll, libfiber_cpp.dll 及静态导出库:lib_acl.lib, lib_protocol.lib lib_acl_cpp.lib, libfiber.lib, libfiber_cpp.lib。</ p >
297299< h2 id ="2-2、将-Acl-库集成到-QT-项目中 "> < a href ="#2-2、将-Acl-库集成到-QT-项目中 " class ="headerlink " title ="2.2、将 Acl 库集成到 QT 项目中 "> </ a > 2.2、将 Acl 库集成到 QT 项目中</ h2 > < p > 参考 lib_fiber/samples-gui/QtFiber/CMakeLists.txt 文件,将 Acl 库的头文件包含进去,如下:</ p >
@@ -317,17 +319,30 @@ <h3 id="2-3-2、在界面中创建协程"><a href="#2-3-2、在界面中创建
317319< figure class ="highlight c++ "> < table > < tr > < td class ="gutter "> < pre > < span class ="line "> 1</ span > < br > < span class ="line "> 2</ span > < br > < span class ="line "> 3</ span > < br > < span class ="line "> 4</ span > < br > < span class ="line "> 5</ span > < br > < span class ="line "> 6</ span > < br > < span class ="line "> 7</ span > < br > < span class ="line "> 8</ span > < br > < span class ="line "> 9</ span > < br > < span class ="line "> 10</ span > < br > < span class ="line "> 11</ span > < br > < span class ="line "> 12</ span > < br > </ pre > </ td > < td class ="code "> < pre > < code class ="hljs c++ "> < span class ="hljs-type "> char</ span > buf[< span class ="hljs-number "> 8192</ span > ];< br > < span class ="hljs-keyword "> while</ span > (< span class ="hljs-literal "> true</ span > ) {< br > < span class ="hljs-type "> int</ span > ret = < span class ="hljs-built_in "> acl_fiber_recv</ span > (conn_, buf, < span class ="hljs-built_in "> sizeof</ span > (buf) - < span class ="hljs-number "> 1</ span > , < span class ="hljs-number "> 0</ span > );< br > < span class ="hljs-keyword "> if</ span > (ret == < span class ="hljs-number "> -1</ span > ) {< br > < span class ="hljs-keyword "> break</ span > ;< br > }< br > < br > buf[ret] = < span class ="hljs-number "> 0</ span > ;< br > < span class ="hljs-keyword "> if</ span > (< span class ="hljs-built_in "> acl_fiber_send</ span > (conn_, buf, ret, < span class ="hljs-number "> 0</ span > ) != ret) {< br > < span class ="hljs-keyword "> break</ span > ;< br > }< br > }< br > </ code > </ pre > </ td > </ tr > </ table > </ figure >
318320
319321< h3 id ="2-3-3、界面程序退出前需要停止协程调度 "> < a href ="#2-3-3、界面程序退出前需要停止协程调度 " class ="headerlink " title ="2.3.3、界面程序退出前需要停止协程调度 "> </ a > 2.3.3、界面程序退出前需要停止协程调度</ h3 > < p > 必须保证在界面程序退出前停止协程调度器,否则界面程序无法正常退出,该步骤也非常重要。可以在主界面处理类里重载基类的 < code > void closeEvent(QCloseEvent *event);</ code > 方法,在该方法里停止协程调度器,如下:</ p >
320- < figure class ="highlight c++ "> < table > < tr > < td class ="gutter "> < pre > < span class ="line "> 1</ span > < br > < span class ="line "> 2</ span > < br > < span class ="line "> 3</ span > < br > < span class ="line "> 4</ span > < br > < span class =" line " > 5 </ span > < br > </ pre > </ td > < td class ="code "> < pre > < code class ="hljs c++ "> < span class ="hljs-function "> < span class ="hljs-type "> void</ span > < span class ="hljs-title "> MainWindow::closeEvent</ span > < span class ="hljs-params "> (QCloseEvent *event)</ span > </ span > < br > < span class =" hljs-function " > </ span > {< br > acl::fiber::< span class ="hljs-built_in "> schedule_stop</ span > (); < span class ="hljs-comment "> // 停止协程调度器</ span > < br > event->< span class ="hljs-built_in "> accept</ span > (); < span class ="hljs-comment "> // 接受关闭事件</ span > < br > }< br > </ code > </ pre > </ td > </ tr > </ table > </ figure >
322+ < figure class ="highlight c++ "> < table > < tr > < td class ="gutter "> < pre > < span class ="line "> 1</ span > < br > < span class ="line "> 2</ span > < br > < span class ="line "> 3</ span > < br > < span class ="line "> 4</ span > < br > </ pre > </ td > < td class ="code "> < pre > < code class ="hljs c++ "> < span class ="hljs-function "> < span class ="hljs-type "> void</ span > < span class ="hljs-title "> MainWindow::closeEvent</ span > < span class ="hljs-params "> (QCloseEvent *event)</ span > </ span > {< br > acl::fiber::< span class ="hljs-built_in "> schedule_stop</ span > (); < span class ="hljs-comment "> // 停止协程调度器</ span > < br > event->< span class ="hljs-built_in "> accept</ span > (); < span class ="hljs-comment "> // 接受关闭事件</ span > < br > }< br > </ code > </ pre > </ td > </ tr > </ table > </ figure >
321323
322- < h2 id ="2-4、小结 "> < a href ="#2-4、小结 " class ="headerlink " title ="2.4、小结 "> </ a > 2.4、小结</ h2 > < p > 以上便是如何编译集成 Acl 协程到 QT 界面程序的方法,主要的要点是:</ p >
324+ < h3 id ="2-3-4、在界面线程中下载数据 "> < a href ="#2-3-4、在界面线程中下载数据 " class ="headerlink " title ="2.3.4、在界面线程中下载数据 "> </ a > 2.3.4、在界面线程中下载数据</ h3 > < p > 点击主界面中点击HTTP下载按钮,在事件处理函数中创建协程从后端HTTP服务器下载数据,过程如下:</ p >
325+ < figure class ="highlight c++ "> < table > < tr > < td class ="gutter "> < pre > < span class ="line "> 1</ span > < br > < span class ="line "> 2</ span > < br > < span class ="line "> 3</ span > < br > < span class ="line "> 4</ span > < br > < span class ="line "> 5</ span > < br > < span class ="line "> 6</ span > < br > < span class ="line "> 7</ span > < br > < span class ="line "> 8</ span > < br > < span class ="line "> 9</ span > < br > < span class ="line "> 10</ span > < br > < span class ="line "> 11</ span > < br > < span class ="line "> 12</ span > < br > < span class ="line "> 13</ span > < br > < span class ="line "> 14</ span > < br > < span class ="line "> 15</ span > < br > < span class ="line "> 16</ span > < br > < span class ="line "> 17</ span > < br > < span class ="line "> 18</ span > < br > </ pre > </ td > < td class ="code "> < pre > < code class ="hljs c++ "> < span class ="hljs-function "> < span class ="hljs-type "> void</ span > < span class ="hljs-title "> MainWindow::onUrlGet</ span > < span class ="hljs-params "> ()</ span > </ span > {< br > ...< br > go[< span class ="hljs-keyword "> this</ span > ] {< br > < span class ="hljs-type "> const</ span > < span class ="hljs-type "> char</ span > *url = < span class ="hljs-string "> "http://www.baidu.com/"</ span > ;< br > < span class ="hljs-function "> acl::http_request < span class ="hljs-title "> req</ span > < span class ="hljs-params "> (url)</ span > </ span > ;< br > < span class ="hljs-keyword "> if</ span > (!req.< span class ="hljs-built_in "> request</ span > (< span class ="hljs-literal "> nullptr</ span > , < span class ="hljs-number "> 0</ span > )) {< br > < span class ="hljs-built_in "> printf</ span > (< span class ="hljs-string "> "Send HTTP request failed\r\n"</ span > );< br > < span class ="hljs-keyword "> return</ span > ;< br > }< br > acl::string body;< br > < span class ="hljs-keyword "> if</ span > (!req.< span class ="hljs-built_in "> get_body</ span > (body)) {< br > < span class ="hljs-built_in "> printf</ span > (< span class ="hljs-string "> "Get HTTP body error\r\n"</ span > );< br > < span class ="hljs-keyword "> return</ span > ;< br > }< br > < span class ="hljs-built_in "> qDebug</ span > () << < span class ="hljs-string "> "Got body:"</ span > << body.< span class ="hljs-built_in "> c_str</ span > ();< br > ...< br > };< br > }< br > </ code > </ pre > </ td > </ tr > </ table > </ figure >
326+
327+ < h3 id ="2-3-5、在协程中延迟创建窗口 "> < a href ="#2-3-5、在协程中延迟创建窗口 " class ="headerlink " title ="2.3.5、在协程中延迟创建窗口 "> </ a > 2.3.5、在协程中延迟创建窗口</ h3 > < p > 如果想某个窗口延迟创建,不必借助定时器,直接在协程中就可以轻松实现:</ p >
328+ < figure class ="highlight c++ "> < table > < tr > < td class ="gutter "> < pre > < span class ="line "> 1</ span > < br > < span class ="line "> 2</ span > < br > < span class ="line "> 3</ span > < br > < span class ="line "> 4</ span > < br > < span class ="line "> 5</ span > < br > < span class ="line "> 6</ span > < br > < span class ="line "> 7</ span > < br > < span class ="line "> 8</ span > < br > </ pre > </ td > < td class ="code "> < pre > < code class ="hljs c++ "> < span class ="hljs-function "> < span class ="hljs-type "> void</ span > < span class ="hljs-title "> MainWindow::delayCreate</ span > < span class ="hljs-params "> ()</ span > </ span > {< br > go[< span class ="hljs-keyword "> this</ span > ] {< br > acl::fiber::< span class ="hljs-built_in "> delay</ span > (< span class ="hljs-number "> 5000</ span > ); < span class ="hljs-comment "> // 休眠 5 秒</ span > < br > < span class ="hljs-function "> InputDialog < span class ="hljs-title "> dialog</ span > < span class ="hljs-params "> (< span class ="hljs-keyword "> this</ span > )</ span > </ span > ;< br > dialog.< span class ="hljs-built_in "> exec</ span > ();< br > };< br > < span class ="hljs-built_in "> qDebug</ span > () << < span class ="hljs-string "> "Fiber was created to create one window after a while"</ span > ;< br > }< br > </ code > </ pre > </ td > </ tr > </ table > </ figure >
329+
330+ < h2 id ="2-4、效果展示 "> < a href ="#2-4、效果展示 " class ="headerlink " title ="2.4、效果展示 "> </ a > 2.4、效果展示</ h2 > < p > 编译运行 acl/lib_fiber/samples-gui/QtFiber/ 工程,可以得到以下运行界面:< br > < img src ="/img/fiber_qt.jpg " srcset ="/img/loading.gif " lazyload alt ="fiber_qt "> </ p >
331+ < ul >
332+ < li > 在前面窗口中,右边请求HTTP服务器时的HTTP请求头,右连接为后端服务器返回的HTTP响应头,该下载过程中在协程中进行,运行结果显示在主界面上;</ li >
333+ < li > 窗口下方的进度条为客户端协程与服务端协程交互时的交互进度展示。</ li >
334+ </ ul >
335+ < h2 id ="2-5、小结 "> < a href ="#2-5、小结 " class ="headerlink " title ="2.5、小结 "> </ a > 2.5、小结</ h2 > < p > 以上便是如何编译集成 Acl 协程到 QT 界面程序的方法,主要的要点是:</ p >
323336< ul >
324337< li > 需要使用 vc2019 编译 Acl 的动态库,并集成至 QT 界面程序的工程文件中;</ li >
325- < li > 编程时需要注意两点 :< ul >
338+ < li > 编程时需要注意 :< ul >
326339< li > 在启动 QT (即调用 app.exec())前,需要先启动 Acl 协程调度器;</ li >
327- < li > 在主界面类里需要重载基类关闭虚方法 < code > closeEvent()</ code > ,并在该方法里停止 Acl 协程调度器。</ li >
340+ < li > 在主界面类里需要重载基类关闭虚方法 < code > closeEvent()</ code > ,并在该方法里停止 Acl 协程调度器;</ li >
341+ < li > 因为协程运行在界面的线程空间中,所以可以在协程中直接操作界面上的窗口对象,避免了线程之间的消息传递过程。</ li >
328342</ ul >
329343</ li >
330344</ ul >
345+ < p > < strong > 注:</ strong > Acl库下载:< a target ="_blank " rel ="noopener " href ="https://github.com/acl-dev/acl/ "> https://github.com/acl-dev/acl/</ a > </ p >
331346
332347
333348 </ div >
@@ -418,6 +433,12 @@ <h2 id="2-4、小结"><a href="#2-4、小结" class="headerlink" title="2.4、
418433 < article class ="post-prev col-6 ">
419434
420435
436+ < a href ="/2025/03/31/fiber_pool/ " title ="Acl 协程池使用指南 ">
437+ < i class ="iconfont icon-arrowleft "> </ i >
438+ < span class ="hidden-mobile "> Acl 协程池使用指南</ span >
439+ < span class ="visible-mobile "> 上一篇</ span >
440+ </ a >
441+
421442 </ article >
422443 < article class ="post-next col-6 ">
423444
0 commit comments