c++ - 如何在 Qt 中设计异步包装器返回值?

标签 c++ qt asynchronous design-patterns qfuture

我在 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 处理它们。课外的 s 还是很笨重的。
  • 为每个方法创建一个单独的类。如果我没记错的话,这是 命令模式 .虽然这会大大增加类(class)的数量,但我相信这是最干净的解决方案。

  • 在 Qt 中设计这种异步包装器的正确方法是什么?

    最佳答案

    我认为命令模式可以在这里工作。

    从抽象命令接口(interface)开始:

    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
    

    关于c++ - 如何在 Qt 中设计异步包装器返回值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59421101/

    相关文章:

    c++ - 显示字符串的最后 n 个字符

    c++ - 大数除法

    c++ - 字符串未被循环修改

    c++ - ios 应用程序使用 C++ 访问文件

    c++ - __do_global_ctors 段错误在项目的某处,无法找到它

    java - 无法在 Spring Boot 中使用 @Async

    c++ - 用于项目交互的 Qt 边界矩形/形状

    c++ - 链接 Qt Creator 插件编辑器/荧光笔

    php - 使用 Ajax 的异步 PHP 查询?

    javascript - 无法在 Node.js 循环中设置超时