QT中使用多线程的几点总结

1.开启线程,QT中有两种开启线程的方式:

  a.继承QThread类,重载run方法

  b.继承QObject对象,使用moveToThread方法改变对象附着的线程(注意,在创建对象时父指针必须为空,且只能将对象从当前线程移动到其他线程,而不能从其他线程移动到当前线程),然后通过信号槽触发相关执行代码

  两种方法的比较:第一种方式只有run方法的代码会运行在新开的线程中,槽函数还是会运行在原先的线程中,所以如果自定义对象中有run方法和槽函数同时调用的方法或变量,需要进行加锁。而方式二槽函数运行在新的线程中,为开发人员省去了很多顾虑,且是官方推荐的用法

2.线程的结束

  通过QThread自定义的finished信号和deleteLater槽进行删除

3.线程的生命周期

  不难发现,两种方式实际上都是在使用QThread对象开启线程,线程本身会在运行完自己的逻辑后退出,但是QThread对象本身也是一个C++类对象,符合一般对象的生命周期管理,那么QThread对象到底是什么时候被释放呢?写一下代码就会发现,虽然开启了新的线程,但线程对象本身是在主线程中,主线程中QThread对象很快会被释放,这时程序会报错:Thread destoryed while....也就是说:线程的生命周期受到线程对象的生命周期影响,我们要保证线程对象的生命周期大于线程的生命周期,解决也很简单,使用new分配内存就可以了,释放deleteLater方法会完成

{
    QThread th1;
    MyObject *myObj;
    myObj->moveToThread(&th1);
}    //th1生命周期结束

MyObject.cpp
{
    ...

    void run
    {
        //业务代码,执行完退出
    }
}

4.参数传递

  在多线程中,通过信号槽传递参数,但是参数类型只支持QMetaObject类型,如果要传递自定义参数类型,先使用qRegisterMetaType("typename");将类型注册为QMetaObject类型

5.多线程操作SQLite

  SQLite只支持多线程读,不支持多线程写,且要在不同线程中使用不同的数据库连接(即QSQLDatabase对象)。注意在创建QSQLDatabase对象时要指定不同的连接名称,否则会将连接作为默认连接自动替代之前的连接,即使用的还是同一连接。

6.QObject对象子对象的创建必须与父对象在同一线程中,使用第一种方式时,在自定义类中不能将this作为父指针传给其他对象(因为QThread创建是在主线程中)

7.关于锁

  涉及到共享数据,常用的有QMutex、QReadLocker、QWriteLocker、QReadWriteLock。

  首先QMutex就是互斥量,用于控制一段代码的执行权,只要一个线程获取了mutex,其他线程再获取都会阻塞。QReadLocker、QWriteLocker原理与QMutex类似,只不过对于获取锁的方式进行了区分(读锁和写锁)。通常我们在逻辑上将一个锁(QReadWriteLock)和一个共享数据进行绑定(即读写该数据时全部获取锁进行判断,且该锁只能用于该数据的判断,读数据用读锁,写数据用写锁),

QReadLocker readLocker(&mReadWriteLock);    //请求获取读锁
QWriteLocker writeLocker(&mReadWriteLock);    //请求获取写锁

那么一共有四种情况:锁当前被一线程中的读/写锁获取,收到另一线程的读/写锁请求;只有当前被读锁获取且又收到读锁请求不会阻塞,以此来展开业务逻辑。获取到锁后,当前locker对象生命周期结束时,自动释放锁。

注意:读写锁的判断效率要比互斥量低,应保守使用。

 

你可能感兴趣的:(QT,C++,QT,多线程,SQLite)