zoukankan      html  css  js  c++  java
  • QT5 QThread

    QT5 QThread

    https://blog.csdn.net/zy19940906/article/details/54412600


     QThread的线程用法上与std::thread相比有较大的区别,4.4版本之前是继承的方式来使用线程(个人猜测可能是因为那会儿c++11还没出来,std::function和std::bind没有,所以继承是实现消息回调比较方便的方式,当然仅仅是猜测,有兴趣可以查证),但4.4之后开始,官方建议不要再用继承的方式来使用线程,而是通过信号槽的方式来取代。

     测试环境:Qt5.7,vs2015。

    一、QThread:
     如下图所示:

    看不清楚图可以直接在官网看:传送门
     1、继承自QObject(截图没截上)

     2、启动线程:start()函数,启动后调用run()函数,run()执行完之后退出线程。

     3、wait:有点类似std::thread 的join,但是需要指定时间,并且不是线程run函数结束后自动返回,如果不指定,默认会一直等待。所以我一般在用的过程中,需要退出的时候(或者调用quit),再wait。

    二、两种用法:
    既然QThread有两种用法,那么就简单介绍下吧:
    图里已经说得很清楚了,那我就不在多说:

    头文件:

    class QtThreadFuncByThread : public QThread
    {
    public:
        void SetLoop(int loop);
        int GetSum();
    
    protected:
        virtual void run();
    
    private:
        int _Loop = 0;
        int _Sum  = 0;
    };
    void QtThreadFuncByThread::SetLoop(int loop)
    {
        this->_Loop = loop;
    }
    
    int QtThreadFuncByThread::GetSum()
    {
        return this->_Sum;
    }
    
    void QtThreadFuncByThread::run()
    {
        for(int i = 0; i < this->_Loop; ++i)
        {
            this->_Sum++;
        }
        int id =(int)QThread::currentThreadId();
        emit Log::GetInstance()->LogStr(QString("qt继承方式子线程id:   %1;").
                                        arg(id));
    }

    使用:

        QtThreadFuncByThread thread;
        thread.SetLoop(loop);
        thread.start();
        thread.wait(100);


     2、信号槽方式(推荐用法):
    至于为啥推荐呢,直接给个传送门吧:传送门

    至于代码,官网给了例子:传送门
    我再写个简化版的:

        QThread * thread = new QThread;
        QtThreadFuncClass* funcclass = new QtThreadFuncClass;
        funcclass->SetLoop(loop);
        funcclass->moveToThread(thread);
        QObject::connect(thread, &QThread::started, funcclass, &QtThreadFuncClass::ThreadFunc, Qt::DirectConnection);
        thread->start();
        thread->wait(100);

     简而言之,你需要做的是把QObject对象,movetothread去,否则你调用的信号槽仍然是在主线程。

    二、互斥量,锁,条件变量,原子操作及其他:
     其实std::thread有的那些互斥量,自解锁,条件变量,future,原子操作等,Qt里面也能找到对应的类,只是用法,和一些细节性的功能不太一致,其他大致都是相似的,所以我就简要的整理和汇总下:

     1、互斥量与自解锁:
    如下图所示:

     qt的互斥量只有简单的QMutex,当然,某种程度上是std::thread里那几种结合体,自解锁也只有一种,如果不记得std::thread有哪些,请看我上一篇。用法比较简单,我就不贴代码了。

     2、读写锁与其自解锁:

     所谓读写锁,顾名思义,即读锁定状态与写锁定状态是不一样的。例如指定某段读取区域为lockForRead(),则表示这段代码仅仅是对资源进行读取,没有改变,所有线程可以共享该资源,无需阻塞;另一段改写某资源区域为LockForWrite(),则表示这段代码需要改写资源,其他线程需要阻塞,同时,lockForRead()锁定的读取资源代码也会被锁住,别的线程无论是读还是写会阻塞

     3、信号量:

     图中其实把函数介绍的比较清楚了,即先指定一定数量的信号量,在实际用的过程中可用于一个类似生产者消费者模式,一个线程负责生产,如果信号量没有空位置了,rlease(加一个位置),另一个线程负责消费,如果有空位置,就消费一个(acquire)。

     4、条件变量:

     前面两个读写锁和信号量这些还和std::thread提供的机制有区别(因为没有,当然,想实现也可以用互斥量这些自己写一个)。条件变量几乎是没啥差异,用法也都差不多,函数已经在图中列出来了。

     5、原子操作:

    官网传送门:传送门
     一共就如图所示只有三种,int,integer,pointer,用法也没太大的区别,不记得在上一篇有没有说过,原子对象本身也是带锁的,多线程访问的时候不用担心上锁问题,是为细粒度锁(mutex这些是粗粒度锁)。

     6、future:

    qfuture依赖于Qt Concurrent,下图接上图:

    官网传送门:传送门
    简单的说就是一个异步方法,类似std::async,只执行一次的异步方法,具体概念用法图中已经解释了。
    下图解释下QFuture和QFutureWatcher关系(官网例子):


     7、QThreadPool:
    其实这个不算线程机制,不应该放上来,随便看看:


    三、一张图:
    该图能较好说明QThread的一些用法及生命周期。
     如下图所示

    参考资料:传送门

     本次其实还有许多的东西可以说,只是做了个简单的图表,慢慢来,现在没有时间补充,总结完这些,其实理解上的话也比较简单,暂时就先这样,当做挖个坑吧= =。

    ========================= End

  • 相关阅读:
    【版本控制工具】 Git进阶1
    【版本控制工具】 Git基础
    问题:com.alibaba.dubbo.rpc.RpcException: Failed to invoke ......
    互联网安全架构之常见的Web攻击手段及解决办法
    【Spring Boot】七、整合actuator实现监控管理
    问题:tomcat启动后,可以访问主页面,但是无法访问dubbo-admin
    【Spring Boot】六、整合dubbo(注解的方式)
    这篇文章,彻底搞懂八大开源框架源码
    Spring Cloud Greenwich.SR4 发布了,跟不上了……
    手把手教你画架构图,看一次就会了!
  • 原文地址:https://www.cnblogs.com/lsgxeva/p/12512438.html
Copyright © 2011-2022 走看看