在View层,我们不可避免的需要一些多线程的控制,但是,却又没有达到抽象出一个线程给程序全局使用的程度,这个线程仅仅只需要负责本view内部的操作,这个线程的生命周期一般短于view的生命周期。所以,我们需要一个简单的方式。这里主要涉及到如下几种需求:
时间短,即使多算一遍、几遍也无所谓;
时间很久,没必要完成前一次;
时间不一定,但是需要排队;
对于第一种需求,我们可以使用Future工具类,或者std::thread func并detach,避免写一个单独的thread subclass,不需要关心new/delete问题。然后让线程通知主UI线程,这里可以不使用Qt的信号槽机制,而是采用主动刷新机制,在view的事件callback函数中检查线程计算是否完成,然后由UI主线程主动读取线程保存的结果,刷新到控件上;或者也可以采用Qt的信号槽机制,先connect一个跨线程的connection,然后在view下一次事件循环中,首先更新view。因为线程对象只能在 slot 中被访问,所以,除了线程、主线程共享对象,否则难以控制该线程。
template<typename R>
bool is_ready(std::future<R> const& f)
{ return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready; }
对于第二种情况,我们就需要在view中持有这个线程,那么,我们就需要关心线程是否处于running 状态,并且能够向线程发送消息,让线程能够判断条件,并能够及时的退出循环,或者再次运行。是否使用同一个线程,亦即是否需要开多个线程,并不是重要的事情。重要的是如何中断前一个工作线程。因为std::thread 自身并没有线程是否结束的flag,如果想要使用它,就必须额外使用变量。在线程run() 内部及时判断flag,尽早退出。可以使用QThread subclass,flag 作为member。或者使用 std::promise联合std::thread, 其实也相当于使用另外一个变量。把future 对象作为member,在slot中判断 wait future 并 忙等其结束,或者用户驱动等待上一个线程结束,或者不等待,直接新开一个线程。
对于第三种情况,比较方便方式是使用单独的 Thread subclass,需要关心new/delete 问题。 可以使用mutex、condition_variable,作为跨线程通信的方法。
由于std::thread 刚被创建出来就开始运行,如果想要在两个线程之间共享参数,就只能通过线程函数参数传入,总归是有点不方便。要是自己再封装class,感觉不方便。QThread就相当于是std::thread封装了一下,可以控制线程开始运行的时机,subclass内部可以有成员变量,供线程使用,也可以由其他线程访问,所以,在Qt项目中,能够直接用QThread就直接用吧。
https://zh.cppreference.com/w/cpp/thread/future
https://doc.qt.io/qt-5/qfuture.html
https://www.qt.io/blog/2017/04/18/multithreaded-programming-future-promise