V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
Mmmmc
V2EX  ›  问与答

关于 Qt 的多线程问题.

  •  1
     
  •   Mmmmc · 2018-05-25 08:59:50 +08:00 · 1634 次点击
    这是一个创建于 2369 天前的主题,其中的信息可能已经有所发展或是发生改变。

    目前要改写一个测试软件, 之前只能是一台一台测, 现在要改写 1 对 20 这样的测试.只需让其他 20 个硬件连接主机就可以了.

    问题来了, 我把之前的逻辑代码 和界面分离开了, 想用多线程去实现. 在用 QThread 的子类的时候, run()函数怎么处理?

    难道把逻辑代码都放进 run()里? 还是调用函数指针? 对了,class 里的十几行全局变量往哪放 .....

    感谢看帖的大佬, 本人 9.9 成新. 希望多多批评. 抱拳

    class Thread : public QThread
    {
    Q_OBJECT
    public:
    Thread();
    virtual void run();
    signals:
    void send(QString msg); //测试结果 发送给 界面 private:
    //十几个变量

    };

    10 条回复    2018-05-28 14:01:17 +08:00
    dirtycold
        1
    dirtycold  
       2018-05-25 09:25:02 +08:00   ❤️ 1
    基本就两种形式,一种是继承 QThread 然后实现 run(),另一种是继承 QObject 外加实现一些信号槽,配合 QObject 的 moveToThread 使用。
    参见 https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
    xiao17174
        2
    xiao17174  
       2018-05-25 12:03:50 +08:00   ❤️ 1
    结合你的需求,建议用楼上说的第二种形式.代码上看起来麻烦一点.逻辑也有点绕,不过最适合你的情况.
    当然,你用的就是第一种形式.所以针对你这种形式产生的问题,以下回答:
    逻辑代码当然是放进 run()里,不然多线程的意义在哪.
    用继承的方式实现线程,有一点要特别注意,你的子类 Thread 除了 run()里的代码是跑在新线程里.其它的信号槽都是在父线程里的.这个逻辑上的区别不知道怎么解释给萌新听,结果就是很多时候程序没有按照你想的方式运行.
    引用官方对这继承方式的警告:It is important to remember that a QThread instance lives in the old thread that instantiated it, not in the new thread that calls run(). This means that all of QThread's queued slots will execute in the old thread. Thus, a developer who wishes to invoke slots in the new thread must use the worker-object approach; new slots should not be implemented directly into a subclassed QThread.

    变量问题,不懂你的疑问点在哪.如果这些变量是专属于某个硬件的,那么放进这个类里.如果是十几个硬件共享的数据,那么放进一个全局单例中共享访问.记得加锁.
    Mmmmc
        3
    Mmmmc  
    OP
       2018-05-25 16:09:00 +08:00
    @xiao17174
    首先感谢大佬的指点, 还有几个问题想问问....
    我的第一个问题是:
    之前的逻辑代码, 都是一个模块一个模块的, 互相调用, 大家都是这么写的, 简洁舒服. 可是我在 run()函数里怎么写?声明放哪?

    比如:
    原来是 Widget 类,
    函数的声明 readIRTInfo();
    函数的实现 Widget::readIRTInfo(){ };


    现在是 Thread 类 ,
    声明 readIRTInfo(); //???这样吗?
    run();
    实现 Thread::run(){ //放 run()里 ??这样?

    readIRTInfo(){ }

    }


    看官别笑, qwq

    第二个问题是:
    变量, 测试需要读取设备的信息等, 一条线程负责一个硬件, 那多条线程下来, 变量存储的数据肯定是个个硬件的, 那我的变量是不是直接在 run()里面声明就可以了?

    不知道说清楚没?
    QAQ
    xiao17174
        4
    xiao17174  
       2018-05-25 17:25:22 +08:00   ❤️ 1
    @Mmmmc 你的需求真的适合之前提到的第二种方式.建议了解一下.笑~
    再回到问题:
    1.从你的描述来看,你对编程的理解确实是萌 99 新.所以我已经无从下手去回答你的问题了.
    话虽这么说,还是回答一下,readIRTInfo()的确是放到 run 里.然后 Model 和 View 之间用信号槽通信.实现时注意遵循 QT 推荐的 MV 模式.

    2.变量,说得大一点.这里有一个是面向对象还是面向过程的区别.如果是面向对象的话,是放到类的成员变量里.对应的,类的生存周期就是跟着设备走的.有一个设备就有一个类.设备完成测试就销毁掉类.如果是面向过程的话,是放到 run 里,对应的类的生存周期是跟着程序走,也就是说 10 个线程类一开始就开在那,有一个设备来了,就通知到 run 里,run 里临时声明一组变量(实现上就是一个设备信息的 struct),跑完后就 hold 在那,等着下一个设备到来,memset 一下针对新的设备开始跑.


    总结就是放哪都可以.但这是一个思维模式上的区别.不过看你已经给出的这些信息,你的思考是偏向过程的.可能是 C 看的比较多一点.所以我让你了解的第二种方式,还是先不用看了.笑~(还不到看的时候,以后还有的是时间)

    难得 V2 上有 QT 的问题,尽自己的能力回答一下.不一定都对.
    Mmmmc
        5
    Mmmmc  
    OP
       2018-05-25 17:43:29 +08:00
    @xiao17174
    大哥 QAQ, 真心感谢你的不吝赐教.谢谢.
    我去看看第二种方法.
    Mmmmc
        6
    Mmmmc  
    OP
       2018-05-25 17:43:58 +08:00
    @dirtycold
    感谢大佬 QAQ
    GeruzoniAnsasu
        7
    GeruzoniAnsasu  
       2018-05-25 19:17:21 +08:00   ❤️ 1
    @Mmmmc 首先你得了解了解多线程的 gui 程序如何设计。最重要的一点就是把界面元素用某种状态保存,主线程(绘制线程)通过状态来绘制呈现,而 workers,实际判断的逻辑,通过更改这些状态来影响 ui 结果。这样,ui 线程在被系统调用 paint 不断刷新的时候,实际关心的只有持续存在的状态,而不会实际经过 worker 逻辑,换句话说,worker 里的逻辑也就不会阻塞住 ui 线程。

    你目标的结构应该是这样

    class UIWidget : public QWidget
    {
    QThread *m_worker;
    UIWidget(){ connect(m_worker,&WorkerThread::beebeebeep,this,&UIWidget::passed); /*在某个地方连接好 worker 触发的信号和改变内部状态的 slot*/ }
    bool m_passed;
    onPaint() { if(m_passed) drawPassed(); }
    drawPassed();//画东西
    public slots:
    passed(){ m_passed=true; } // worker 线程通过 emit 对应信号触发 ui 线程的某个 slot,并在 slot 中改变上文提到的状态来影响界面
    }

    class WorkerThread : public QThread
    {
    signals:
    void beebeebeep();
    run(){ /*实现流程*/ }
    readIRTInfo(){ /*某些关键函数*/ }
    }

    省略了巨量细节。请读 qt 文档的 connectiontype,以及各种多线程设计相关,以及各种 oo 的参考书

    再以及。。其实你都可以不需要多线程来完成这个目标
    Mmmmc
        8
    Mmmmc  
    OP
       2018-05-25 19:39:23 +08:00
    @GeruzoniAnsasu

    最后一句话, 实现的方式非常有意思.

    还有
    不用线程, 那就是用事件了.

    大哥打了这么多字,实在感激. QAQ,
    愚蠢的大脑也开始旋转,让我研究研究
    Mmmmc
        9
    Mmmmc  
    OP
       2018-05-25 19:42:22 +08:00
    @GeruzoniAnsasu 少打了几个字...

    不止最后一句话, 全篇实现的方式非常有意思.

    还有
    不用线程, 那就是用事件了.

    大哥打了这么多字,实在感激. QAQ,
    愚蠢的大脑也开始旋转,让我研究研究
    Mmmmc
        10
    Mmmmc  
    OP
       2018-05-28 14:01:17 +08:00
    补一下自己的坑,

    同时再次感谢上面的大佬给出的建议,
    @dirtycold
    @xiao17174
    @GeruzoniAnsasu

    谢谢 QAQ

    关于 多线程下变量的问题, 下面这个帖子说的很系统 :

    QT 事件循环与线程
    https://blog.csdn.net/zxh2075/article/details/50574741
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5630 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 07:53 · PVG 15:53 · LAX 23:53 · JFK 02:53
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.