qt线程

qt4.7 之前的线程操作

代码
需要重写一个类,继承QTread

class mythread:public QThread
{
  Q_OBJECT  //必须加,否则出现一些奇怪问题
public:
  	mytherad();
 protected:
 	void run();
 signals:
    sigDone();
}

void run()
{ QThread::sleep(5);
emit sigdone();
}

如何启动子线程?
主线程中加入这个子线程的头文件,然后定义一个mythread对象 myt;
通过一个函数 myt.start(); 就会启动。当子线程完成操作的时候,可以向父进程发出一个信号,主线程里收到这个信号交给某个函数处理
也就是connect把两个连接起来

这里有个堵塞的方法
如果mythread有个变量,让子进程去修改这个变量,主进程里while一直判断这个变量是否被修改,当子进程被修改了,主进程继续向下进行

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
总结:某个类继承QThread,在这个类里重写run方法,run方法里是想让子线程想要执行的内容
在主线程里面新建这个类的一个对象,然后对象.start() 将会执行
然后一些其他的就是执行完了可以传递信号回到主进程,让主进程里的某个槽函数进行后续的处理

qt4.7 之后的线程操作

代码

特点:灵活的指定一个线程对象的线程入口函数

多线程使用注意事项
1.业务对象,构造的时候不能指定父对象
2.子线程中不能处理ui窗口(ui相关的类)
3.子线程中只能处理一些数据相关的操作,不能涉及窗口

主线程:

OtherTread类包含:
信号:结束信号
槽函数:子线程进程进行工作的函数

主线程包含:
子线程的头文件
信号:发出子线程开始工作的函数
槽函数:接收子线程完成工作的函数
QThread *qt;
函数 发送要子线程开始工作的函数
app里面:
OtherTread *otherqt =new OtherTread();
qt=new QThread ();
otherqt->moveToThread(qt)
qt->start();
connnect(this,SIGNAL(让子线程开始的函数),otherqt ,SLOT(子线程工作的函数))
connect(子线程完成与当前的某个槽函数连接)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
总结:
和上面一样,新建一个想要执行逻辑的类的对象,再新建一个QThread对象 第一个对象->moveToThread(进程对象) 然后在父进程里面就控制了这个新写的类对象的进程,让QThread对象启动,这时的启动并没有真正的启动,需要用信号槽机制发送信号以及绑定需要执行的函数

这样做的目的是可以将任意的对象选择性的交给任意的进程对象。

子线程的关闭

一般:
可以用信号槽的方式将窗口析构与子进程销毁连接上
connect(this, &class的名::destroyed,this,&class的名::一个槽函数)
这个槽函数里面
{
子线程对象.quit()
子线程对象.wait() //等待线程的工作完成
}

需要注意的是,前提是子线程的工作已经运行完
而且这个子线程是新建的线程对象,并非某个class的对象

下面代码的问题: 局部对象t在start后就会被销毁,同时成员变量 i 也会被销毁,然而线程还在运行,非法访问已经被销毁的变量
在这里插入图片描述
同步型线程设计

在析构函数中先调用wait()函数,强制等到线程运行结束
这种设计适合 线程生命期相对较短的情形

SyncThread::~SyncThread()
{
    wait(); // 等待线程结束才析构
 
    qDebug() << "SyncThread::~SyncThread() destroy thread object";
}

异步型线程设计

在 run() 中最后调用 deleteLater() 函数 ,线程体函数主动申请销毁线程对象
这种设计适合线程生命期不可控,需要长时间运行于后台情形

void AsyncThread::run()
{
    qDebug() << "void AsyncThread::run() tid = " << currentThreadId();
 
    for(int i=0; i<3; i++)
    {
        qDebug() << "void AsyncThread::run() i = " << i;
 
        sleep(1);
    }
 
    qDebug() << "void AsyncThread::run() end";
 
    deleteLater(); // 通知销毁当前线程对象 void QObject::deleteLater () [slot]
}

进程同步

比如有个加法运算(加法运算类里有三个值,前两个值是要运算的数,第三个值是他们加后的结果,第三个值默认为0)
在主进程里新建三个子进程让他们执行各自的加法运算
最后在主进程里面将这三个对象的第三个值也就是结果再加起来

在这里重点说明一下:只要一个类继承了QThread 而且还重写了run方法 如果在某个地方有三个这个类的对象,那么在调用各个对象的start后,那么将会是用子进程进行处理的,不再是顺序的调用了,哪个进程先执行完无法获悉

如果想要同步,就需要用的wait(0
这里注意wait和sleep的区别
sleep()方法的作用是 让当前正在执行的线程休眠指定的毫秒数
wait()方法
在这里插入图片描述
这个图片是说在哪里对某个对象调用wait,哪里会等待那个对象执行完毕
比如上面有个对象a执行了加法运算,那么再调用a.wait()便是等待a完成以后再向下进行,因此说,上面说的那个堵塞的方法是极其愚蠢的

线程锁

QMutex中的关键成员函数
void lock()
当锁空闲时,获取锁并执行
当锁被获取,则自身会堵塞并等待锁被释放
void unlock()
释放锁
需要注意的是:同一把锁的加锁和释放锁必须在同一线程中出现

伪代码例子


#include <QtCore/QCoreApplication>
#include <QThread>
#include <QMutex>
#include <QDebug>
static QMutex g_mutex;  // 线程锁
static QString g_store; // 仓库
class Producer : public QThread
{
protected:
    void run()
    { int count = 0;
        while(true)
        {
            g_mutex.lock();
            g_store.append(QString::number((count++) % 10));
            qDebug() << objectName() << " : " + g_store;
            g_mutex.unlock();
            msleep(1);
        }
    }
};
 
class Customer : public QThread
{
protected:
    void run()
    {
        while( true )
        {
            g_mutex.lock();
 
            if( g_store != "" )
            {
                g_store.remove(0, 1);
 
                qDebug() << objectName() << " : " + g_store;
            }
 
            g_mutex.unlock();
 
            msleep(1);
        }
    }
};
 
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
 
    Producer p;
    Customer c;
 
    p.setObjectName("Producer");
    c.setObjectName("Customer");
   p.start();
    c.start();
    return a.exec();
}

上述可以看出要先有一把锁mutex,然后对于临界资源加这把锁,对于临界资源加锁并非临界资源有特定的方法,换种说法是临界资源感知不到锁的存在,只是逻辑上加上锁了,此外,每一个临界资源都有特定的一把锁,不能把一个mutex逻辑上给多个临界资源加锁
且需要注意的是,如果用while判断,每次在while结尾需要加上让自身睡上几秒,不要让while一直运行

信号量

操作系统学习的时候,对于信号量和锁会有下面这种体会
在这里插入图片描述

伪代码
QSemaphore g_sem_free(5); // 5个可生产资源
QSemaphore g_sem_used(0); // 0个可消费资源
生产者
g_sem_free.acquire(); g_sem_free信号量减一 acquire获取不到会在这里堵塞
… 主要逻辑段
g_sem_used.release(); g_sem_used信号量 加一
消费者
g_sem_used.acquire();
… 主要逻辑段
g_sem_free.release();

#include <QtCore/QCoreApplication>
#include <QThread>
#include <QSemaphore>
#include <Qdebug>
 
const int SIZE = 5;
unsigned char g_buff[SIZE] = {0}; 
 
QSemaphore g_sem_free(SIZE); // 5个可生产资源
QSemaphore g_sem_used(0);    // 0个可消费资源
 
// 生产者生产产品
class Producer : public QThread
{
protected:
    void run()
    {
        while( true )
        {
            int value = qrand() % 256;
 
            // 若无法获得可生产资源,阻塞在这里
            g_sem_free.acquire();
 
            for(int i=0; i<SIZE; i++)
            {
                if( !g_buff[i] )
                {
                    g_buff[i] = value;
 
                    qDebug() << objectName() << " generate: {" << i << ", " << value << "}";
 
                    break;
                }
            }
 
            // 可消费资源数+1
            g_sem_used.release();
 
            sleep(2);
        }
    }
};
// 消费者消费产品
class Customer : public QThread
{
protected:
    void run()
    {
        while( true )
        {
            // 若无法获得可消费资源,阻塞在这里
            g_sem_used.acquire();
 
            for(int i=0; i<SIZE; i++)
            {
                if( g_buff[i] )
                {
                    int value = g_buff[i];
 
                    g_buff[i] = 0;
 
                    qDebug() << objectName() << " consume: {" << i << ", " << value << "}";
 
                    break;
                }
            }
 
            // 可生产资源数+1
            g_sem_free.release();
 
            sleep(1);
        }
    }
};
 
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
 
    Producer p1;
    Producer p2;
    Producer p3;
 
    p1.setObjectName("p1");
    p2.setObjectName("p2");
    p3.setObjectName("p3");
 
    Customer c1;
    Customer c2;
 
    c1.setObjectName("c1");
    c2.setObjectName("c2");
 
    p1.start();
    p2.start();
    p3.start();
 
    c1.start();
    c2.start();
    
    return a.exec();
}

子线程与主线程通讯的方法

主要思想就是子线程发送信号,父进程提前将信号和自己的某个槽函数绑定

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贪睡的蜗牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值