簡體   English   中英

如何在 Qt 中設計異步包裝器返回值?

[英]How to design asynchronous wrapper returning values in Qt?

我在 Qt 中為外部可執行文件編寫了一個包裝類。 它有很多方法,其中大部分是:

  • 是耗時的。
  • 需要返回不同類型的值。

在這種情況下,同步包裝器非常簡單:

class SyncWrapper {
public:
    // Values are fetched *synchronously* in these methods
    QString name() const;
    QStringList files() const;
    bool remove(const QString &file) const;
};

但是,我想讓這個包裝器異步,像這樣:

class AsyncWrapper {
    Q_OBJECT
public:
    // Values are fetched *asynchronously* in these methods
    void name() const;
    void files() const;
    void remove(const QString &file) const;
signals:
    // Values are returned via signals
    void nameReady(const QString &name) const;
    void filesReady(const QStringList &files) const;
    void removeDone(bool success) const;
};

問題

我不確定這種模式是否可以,因為有很多點與我有關:

  • 復制。 假設有 100 種方法。 這些方法中的每一種都需要一個專用信號。
  • 比賽條件。 如果我多次運行相同的方法,我將無法知道我捕捉到的信號的順序。

可能的解決方案

我想出的其他一些想法:

  • 堅持同步包裝器並在調用類方法時使用QtConcurrent::run 不幸的是,這是一個非常龐大的解決方案。
  • 從方法返回QFuture對象。 同樣,這將需要在類之外使用QFutureWatcher處理它們,這仍然非常龐大。
  • 為每個方法創建一個單獨的類。 如果我沒記錯的話,這是一個命令模式 雖然這將大大增加類的數量,但我相信這是最干凈的解決方案。

在 Qt 中設計這種異步包裝器的正確方法是什么?

我認為命令模式可以在這里工作。

從抽象命令接口開始:

class Command : public QObject
{
    Q_OBJECT
public:
    virtual ~Command() = default;
    virtual void execute() = 0;
signals:
    void done(bool success) const;
};

子類並沒有那么復雜,只需給它們一些狀態並覆蓋execute ,例如

class NameCommand : public Command
{
    QString _name;
public:
    void execute() override
    {
        _name = ... //fetch name
        emit done(true);
    }
    QString name() const { return _name; }
};

或者

class RemoveFileCommand : public Command
{
    QString _filename;
public:
    RemoveFileCommand(const QString filename) : _filename(filename){}
    void execute() override
    {
        //remove _filename

        emit done(true);
    }
};

通過這種方式,您可以構建一組執行多種不同操作的對象,但您可以實現命令路由器並保持異步或不異步運行命令的機會:

class CommandRouter : public QObject
{
    Q_OBJECT
public:
    void run(Command * command, std::function<void(bool)> done, bool async = false)
    {
        connect(command, &Command::done, this, done, (async ? Qt::QueuedConnection : Qt::DirectConnection));
        if(async)
        {
            QtConcurrent::run(command, &Command::execute);
        }
        else
        {
            command->execute();
        }
    }
};

所以你最終會得到類似的東西:

    CommandRouter router;

    RemoveFileCommand removecommand("somefile.tar.gz");
    router.run(&removecommand, [](bool success) {

        qDebug() << "REMOVE " << (success ? "SUCCESSFUL" : "FAILED");

    }, true); //this will run asyncrounously

    NameCommand namecommand;
    router.run(&namecommand, [&namecommand](bool success) {

        if(success)
        {
            qDebug() << "Name: " + namecommand.name();
        }
        else
        {
            qDebug() << "FETCH NAME FAILED";
        }

    }; //this will block

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM