QFuture that can be cancelled and report progress

前端 未结 5 1025
终归单人心
终归单人心 2020-12-13 01:22

The QFuture class has methods such as cancel(), progressValue(), etc. These can apparently be monitored via a QFutureWatcher

5条回答
  •  误落风尘
    2020-12-13 01:51

    Though it's been a while since this question was posted and answered I decided to add my way of solving this problem because it is rather different from what was discussed here and I think may be useful to someone else. First, motivation of my approach is that I usually don't like to invent own APIs when framework already has some mature analogs. So the problem is: we have a nice API for controlling background computations represented by the QFuture<>, but we have no object that supports some of the operations. Well, let's do it. Looking on what's going on inside QtConcurrent::run makes things much clearer: a functor is made, wrapped into QRunnable and run in the global ThreadPool.

    So I created generic interface for my "controllable tasks":

    class TaskControl
    {
    public:
        TaskControl(QFutureInterfaceBase *f) : fu(f) {  }
        bool shouldRun() const { return !fu->isCanceled(); }
    private:
        QFutureInterfaceBase *fu;
    };
    
    template 
    class ControllableTask
    {
    public:
        virtual ~ControllableTask() {}
        virtual T run(TaskControl& control) = 0;
    };
    

    Then, following what is made in qtconcurrentrunbase.h I made q-runnable for running this kind of tasks (this code is mostly from qtconcurrentrunbase.h, but slightly modified):

    template 
    class RunControllableTask : public QFutureInterface , public QRunnable
    {
    public:
        RunControllableTask(ControllableTask* tsk) : task(tsk) { }
        virtial ~RunControllableTask() { delete task; }
    
        QFuture start()
        {
            this->setRunnable(this);
            this->reportStarted();
            QFuture future = this->future();
            QThreadPool::globalInstance()->start(this, /*m_priority*/ 0);
            return future;
        }
    
        void run()
        {
            if (this->isCanceled()) {
                this->reportFinished();
                return;
            }
            TaskControl control(this);
            result = this->task->run(control);
            if (!this->isCanceled()) {
                this->reportResult(result);
            }
            this->reportFinished();
        }
    
        T  result;
        ControllableTask *task;
    };
    

    And finally the missing runner class that will return us controllable QFututre<>s:

    class TaskExecutor {
    public:
        template 
        static QFuture run(ControllableTask* task) {
            return (new RunControllableTask(task))->start();
        }
    
    };
    

    The user should sublass ControllableTask, implement background routine which checks sometimes method shouldRun() of TaskControl instance passed to run(TaskControl&) and then use it like:

    QFututre futureValue = TaskExecutor::run(new SomeControllableTask(inputForThatTask));
    

    Then she may cancel it by calling futureValue.cancel(), bearing in mind that cancellation is graceful and not immediate.

提交回复
热议问题