• <menu id="w2i4a"></menu>
  • logo Qt使用教程2020

    文檔首頁>>Qt使用教程2020>>跨平臺開發(fā)框架Qt最新資訊:Qt6中的異步API

    跨平臺開發(fā)框架Qt最新資訊:Qt6中的異步API


    Qt(發(fā)音為“ cute”,而不是“ cu-tee”)是一個跨平臺框架,通常用作圖形工具包,它不僅創(chuàng)建CLI應(yīng)用程序中非常有用。而且它也可以在三種主要的臺式機操作系統(tǒng)以及移動操作系統(tǒng)(如Symbian,Nokia Belle,Meego Harmattan,MeeGo或BB10)以及嵌入式設(shè)備,Android(Necessitas)和iOS的端口上運行?,F(xiàn)在我們?yōu)槟闾峁┝嗣赓M的試用版。趕快點擊下載Qt最新試用版>>

    點擊獲取更多文章教程

    大家可能知道Qt提供了幾種多線程結(jié)構(gòu)(線程,互斥體,等待條件等),以及更高級別的API,如QThreadPoolQt Concurrent和其他相關(guān)類。在本文中,我們將專注于更高級別的異步API和Qt 6中引入的更改。

    Qt中更高級別的并發(fā)API

    Qt Concurrent通過消除對低級同步(基元,例如互斥鎖和鎖)的需求,并手動管理多個線程,使多線程編程變得更加容易。它為并行處理可迭代容器提供了映射,過濾和歸約算法(從功能編程中可以更好地了解)。此外,還有類QFuture,QFutureWatcher和,QFutureSynchronizer用于訪問和監(jiān)視異步計算的結(jié)果。盡管所有這些都非常有用,但是仍然存在一些缺點,例如無法使用QFuture 在Qt Concurrent之外,缺乏對鏈接多個計算以簡化和簡潔代碼的支持,缺乏Qt Concurrent API的靈活性等。對于Qt 6,目前正在嘗試解決這些問題,并使Qt的多線程編程更加有趣 !

    將延續(xù)附加到QFuture

    多線程編程中的一種常見情況是運行異步計算,這又需要調(diào)用另一個異步計算并將數(shù)據(jù)傳遞給該異步計算,該異步計算依賴于另一個計算,依此類推。由于每個階段都需要上一個階段的結(jié)果,因此您需要等待(通過阻止或輪詢)直到上一個階段完成并使用其結(jié)果,或者以“回調(diào)”的方式構(gòu)造代碼。這些選項都不是完美的:要么浪費資源等待時間,要么獲取復(fù)雜的無法維護的代碼。添加新的階段或邏輯(用于錯誤處理等)會進一步增加復(fù)雜性。

    為了更好地理解問題,讓我們考慮以下示例。假設(shè)我們要從網(wǎng)絡(luò)下載大圖像,對其進行一些繁重的處理,然后在我們的應(yīng)用程序中顯示生成的圖像。因此,我們執(zhí)行以下步驟:

    • 發(fā)出網(wǎng)絡(luò)請求并等待,直到收到所有數(shù)據(jù)
    • 根據(jù)原始數(shù)據(jù)創(chuàng)建圖像
    • 處理圖像
    • 展示下

    對于每個需要依次調(diào)用的步驟,我們都有以下方法:

    QByteArray download(const QUrl &url);
    QImage createImage(const QByteArray &data);
    QImage processImage(const QImage &image);
    void show(const QImage &image);

    我們可以使用QtConcurrent異步運行這些任務(wù)并QFutureWatcher監(jiān)視進度:

    void loadImage(const QUrl &url) {
        QFuture data = QtConcurrent::run(download, url);
        QFutureWatcher dataWatcher;
        dataWatcher.setFuture(data);
        
        connect(&dataWatcher, &QFutureWatcher ::finished, this, [=] {
            // handle possible errors
            // ...
            QImage image = createImage(data);
            // Process the image
            // ...
            QFuture processedImage = QtConcurrent::run(processImage, image);
            QFutureWatcher<QImage> imageWatcher;
            imageWatcher.setFuture(processedImage);
    
            connect(&imageWatcher, &QFutureWatcher::finished, this, [=] {
                // handle possible errors
                // ...
                show(processedImage);
            });
        });
    }

    我們要添加到鏈中的步驟越多越難看。QFuture通過添加對通過QFuture::then()方法附加延續(xù)的支持,可以幫助解決此問題:

    auto future = QtConcurrent::run(download, url)
                .then(createImage)
                .then(processImage)
                .then(show);

    這無疑看起來要好得多!但是缺少一件事:錯誤處理。您可以執(zhí)行以下操作:

    auto future = QtConcurrent::run(download, url)
                .then([](QByteArray data) {
                    // handle possible errors from the previous step
                    // ...
                    return createImage(data);
                })    
                .then(...)    
                ...

    這將起作用,但是錯誤處理代碼仍與程序邏輯混合在一起。另外,如果其中一個步驟失敗,我們可能也不想運行整個鏈。這可以通過使用QFuture::onFailed()方法來解決,該方法允許我們?yōu)槊糠N可能的錯誤類型附加特定的錯誤處理程序:

    auto future = QtConcurrent::run(download, url)
                .then(createImage)
                .then(processImage)
                .then(show)
                .onFailed([](QNetworkReply::NetworkError) {
                    // handle network errors
                })
                .onFailed([](ImageProcessingError) {
                    // handle image processing errors
                })
                .onFailed([] {
                    // handle any other error
                });

    請注意,使用.onFailed()需要啟用異常類。如果任何步驟失敗并發(fā)生異常,則鏈會中斷,并調(diào)用與拋出的異常類型匹配的錯誤處理程序。

    根據(jù)信號創(chuàng)建QFuture

    給定一個帶有signal 的QObject基于類,您可以通過以下方式將此用作Future類:MyObjectvoid mySignal(int)

    QFuture intFuture = QtFuture::connect(&object, &MyObject::mySignal);

    現(xiàn)在,您可以將延續(xù),失敗或取消處理程序附加到最終的結(jié)果上。

    請注意,最終結(jié)果的類型與signal的自變量類型匹配。如果沒有參數(shù),則 返回 QFuture<void>。如果有多個參數(shù),則結(jié)果存儲在中std::tuple。

    讓我們回到圖像處理示例的第一步(即下載),以了解這在實踐中如何有用。有很多方法可以實現(xiàn)它,我們將使用QNetworkAccessManager來發(fā)送網(wǎng)絡(luò)請求并獲取數(shù)據(jù):

    QNetworkAccessManager manager;    
    ...
    
    QByteArray download(const QUrl &url) {        
        QNetworkReply *reply = manager.get(QNetworkRequest(url));
        QObject::connect(reply, &QNetworkReply::finished, [reply] {...});
        
        // wait until we've received all data
        // ...    
        return data;        
    }

    但是上面的阻塞等待不是很好,如果我們可以避開它那就更好了,比如說“當(dāng)QNetworkAccessManager獲取數(shù)據(jù)時,創(chuàng)建一個圖像,然后對其進行處理然后顯示”。我們可以通過將網(wǎng)絡(luò)訪問管理器的finished()信號連接到QFuture:

    QNetworkReply *reply = manager.get(QNetworkRequest(url));
    
    auto future = QtFuture::connect(reply, &QNetworkReply::finished)
            .then([reply] {
                return reply->readAll();
            })
            .then(QtFuture::Launch::Async, createImage)
            .then(processImage)
            .then(show)        
            ...

    您會注意到,現(xiàn)在我們不再使用QtConcurrent::run()異步下載而是在新線程中返回數(shù)據(jù),我們只是連接到QNetworkAccessManager::finished()信號,從而開始了計算鏈。還請注意以下行中的其他參數(shù):

            .then(QtFuture::Launch::Async, createImage)

    默認(rèn)情況下.then()在父進程運行所在的同一線程(在本例中為主線程)中調(diào)用by附加的延續(xù)。現(xiàn)在,我們不再使用QtConcurrent::run()異步啟動鏈,我們需要傳遞附加QtFuture::Launch::Async參數(shù),以在單獨的線程中啟動連續(xù)鏈,并避免阻塞UI。

    創(chuàng)建一個QFuture

    到目前為止,在QFuture內(nèi)部創(chuàng)建和存儲值的唯一“官方”方法是QtConcurrent中的一種方法。所以QtConcurrent以外,QFuture不是很有用。在Qt 6中,將Andrei Golubev引入了“Setter”, QFuture: QPromise的對應(yīng)物。它可用于為異步計算設(shè)置值,進度和異常,以后可通過訪問QFuture。為了演示其工作原理,讓我們再次重寫圖像處理示例,并使用QPromise該類:

    QFuture download(const QUrl &url) {
        QPromise promise;
        QFuture future = promise.future();
        
        promise.reportStarted(); // notify that download is started
        
        QNetworkReply *reply = manager.get(QNetworkRequest(url));
        QObject::connect(reply, &QNetworkReply::finished,
                [reply, p = std::move(promise)] {
                    p.addResult(reply->readAll());
                    p.reportFinished(); // notify that download is finished
                    reply->deleteLater();
                });
        
        return future;
    }
    auto future = download()
            .then(QtFuture::Launch::Async, createImage)
            .then(processImage)
            .then(show)
            ...

    QtConcurrent的變化

    -現(xiàn)在,您可以為QtConcurrent的所有方法設(shè)置自定義線程池,而不是始終在全局線程池上運行它們并可能阻止其他任務(wù)的執(zhí)行。
    -映射和過濾器縮小算法現(xiàn)在可以采用初始值,因此您不必為沒有默認(rèn)構(gòu)造函數(shù)的類型做變通辦法。
    - QtConcurrent::run進行了改進,可以處理可變數(shù)量的參數(shù)和僅移動類型。

    此外,我們在QtConcurrent中添加了兩個新的API,以為用戶提供更大的靈活性。讓我們更詳細(xì)地看一下。

    QtConcurrent :: runWithPromise

    QtConcurrent::runWithPromise()Jarek Kobus開發(fā)的新方法是QtConcurrent框架的另一個不錯的補充。它非常類似于QtConcurrent::run(),不同之處在于,它使QPromise與給定任務(wù)相關(guān)聯(lián)的對象可供用戶訪問。

    auto future = QtConcurrent::runWithPromise(
                [] (QPromise &promise, /* other arguments may follow */ ...) {
                    // ...
                    for (auto value : listOfValues) {
                        if (promise.isCanceled())
                            // handle the cancellation
            
                    // do some processing...
            
                    promise.addResult(...);
                    promise.setProgressValue(...);
                    }
                },
                /* pass other arguments */ ...);

    runWithPromise()用戶可以更好地控制任務(wù),并且可以響應(yīng)取消或暫停請求,進行進度報告等操作,而這些使用QtConcurrent::run()是不可能實現(xiàn)的。

    QtConcurrent ::任務(wù)

    QtConcurrent::task()提供了一個流暢的界面,用于在單獨的線程中運行任務(wù)。它對于QtConcurrent::run()是更為現(xiàn)代的替代方案,并配置任務(wù)的方式也更為方便。您可以使用任何順序指定參數(shù),跳過不需要的參數(shù),等等,而不是使用少數(shù)幾個參數(shù)之一來傳遞參數(shù)來運行任務(wù)。例如:

    QFuture future = QtConcurrent::task(doSomething)
            .withArguments(1, 2, 3)
            .onThreadPool(pool)
            .withPriority(10)
            .spawn();

    請注意,與run()不同,您還可以為任務(wù)傳遞優(yōu)先級。

    本篇文章中的內(nèi)容你都學(xué)會了嗎?如果這篇文章沒能滿足你的需求、點擊獲取更多文章教程!現(xiàn)在立刻下載Qt免費試用吧!更多Qt類開發(fā)工具QtitanRibbon、QtitanChartQtitanNavigation、QtitanDockingQtitanDataGrid在線訂購現(xiàn)直降1000元,歡迎咨詢慧都在線客服獲取更多優(yōu)惠>>

    掃碼咨詢


    添加微信 立即咨詢

    電話咨詢

    客服熱線
    023-68661681

    TOP
    三级成人熟女影院,欧美午夜成人精品视频,亚洲国产成人乱色在线观看,色中色成人论坛 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();