一文带你了解Qt多线程的实现方式

 更新时间:2025年01月03日 16:01:46   作者:Liknana  
这篇文章主要为大家详细介绍了Qt多线程的实现方式的相关知识,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下

QThread的run方法

一个QThread对象管理一个线程,一般从QThread继承一个自定义的类,并实现run方法,在run函数中实现线程需要完成的任务。QThread自身定义了started() 和finish()两个信号,started() 信号是在开始执行之前发射,finished()信号是在线程就要结束时发射。

  class WorkerThread : public QThread
  {
      Q_OBJECT
      void run() override {
          // TODO
          emit sigMsg(result);
      }
  signals:
      void sigMsg(const QString &s);
  };

  void main()
  {
      WorkerThread *workerThread = new WorkerThread(this);
      workerThread->start();
  }

特点

1、优点:可以通过信号槽与外界进行通信。

2、缺点:每次新建一个线程都需要继承QThread,实现一个新类,使用不太方便。

要自己进行资源管理,线程释放和删除。并且频繁的创建和释放会带来比较大的内存开销。

3、适用场景:QThread适用于那些常驻内存的任务。

QObject的moveToThread

创建一个继承QObject的类MyThread,把要执行的计算放到一个函数中doWork,然后new一个Qthread,并把创建的myThread类movetothread到创建好的子线程中,然后start子线程,这样就实现了一个子线程。这里一定要通过信号去调用doWork函数。

class MyThread:public QObject
{
    Q_OBJECT
public slots:
    void doWork(){
        int i=0;
        while(i<4){
            // ToDo
            qDebug()<<QThread::currentThread()<<" "<<i++;
        }
    }
​
​
};
​
class MyTest :public QObject
{
    Q_OBJECT
    QThread workerThread;
public:
    MyTest(){
        MyThread* myThread = new MyThread();   // 这个任务函数不能有父对象,有父对象时不可以moveToThread
        myThread->moveToThread(&workerThread);
        workerThread.start();
        connect(this,&MyTest::active,myThread,&MyThread::doWork);  // 一定要通过槽函数去调用对应的函数,否则还是在主线程
    }
    ~MyTest(){
        workerThread.quit();
    }
signals:
    void active();
};
​
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    qDebug()<<QThread::currentThread();
    ui->setupUi(this);
    MyTest* t = new MyTest();
    t->active();
    QThread::sleep(3000);
    t->deleteLater();
}

特点

一定要通过槽函数的形式去调用函数,要注意!你创建的QThread对象实例,仍然存活在主线程上,而非子线程。所以如果你直接调用其中的函数,那么还是在主线程上运行的。该方法并不是线程安全的。

QRunnalble的run

继承Qrunnable,并重写run虚函数,使用QThreadPool启动线程

class Runnable:public QRunnable
{
public:
       Runnable();
       ~Runnable();
       void run();
};

Runnable::Runnable():QRunnable()
{

}

Runnable::~Runnable()
{
       cout<<"~Runnable()"<<endl;
}

void Runnable::run()
{
       cout<<"Runnable::run()thread :"<<QThread::currentThreadId()<<endl;
       cout<<"dosomething ...."<<endl;
}
int main(int argc, char *argv[])
{
       QCoreApplication a(argc, argv);
       cout<<"mainthread :"<<QThread::currentThreadId()<<endl;
       Runnable runObj;
       QThreadPool::globalInstance()->start(&runObj);
       returna.exec();
}

特点

1,无需手动释放资源,QThreadPool启动线程执行完成后会自动释放。

2,不能使用信号槽与外界通信。

3,QRunnable适用于线程任务量比较大,需要频繁创建线程。QRunnable能有效减少内存开销。

QtConcurrent的run

使用QtConcurrent编写的程序会根据可用的处理器内核数自动调整使用的线程数。QtConcurrent::run能够方便快捷的将任务丢到子线程中去执行,无需继承任何类,也不需要重写函数,使用非常简单。通过QtConcurrent::run()返回的QFuture不支持取消、暂停,返回的QFuture只能用于查询函数的运行/完成状态和返回值。

函数原型:

QFuture<T> QtConcurrent::run(Function function, ...)
QFuture<T> QtConcurrent::run(QThreadPool *pool, Function function, ...)

使用方式

    QtConcurrent::run([=]() {
        // TODO
    });

线程同步

基于QMutex互斥同步

QMutex的目的是保护一个对象、数据结构或者代码段,所以同一时间只有一个线程可以访问它。如果使用Mutex锁那么多个线程在访问一段代码的时候是存在阻塞的,一个执行完毕下一个线程才会继续执行

lock():试图锁定互斥量。如果另一个线程已经锁定这个互斥量,那么这次调用将阻塞直到那个线程把它解锁。

unlock():进行解锁

tryLock():试图锁定互斥量。如果锁被得到,这个函数返回真。如果另一个进程已经锁定了这个互斥量,这个函数返回假,而不是一直等到这个锁可用为止

QMutex mutex;
 void DebugInfo()
 {
    mutex.lock();
    qDebug("ABC");
    qDebug("DEF");
    mutex.unlock();
 }

基于QReadWriteLock的线程同步

一种读写锁,用于保护可以进行读写访问的资源。这种索允许多个线程同时进行只读访问,但是一旦一个线程想要写入资源,则必须阻止所有其他线程,直到写入完成。

 QReadWriteLock lock;

 void ReaderThread::run()
 {
     ...
     lock.lockForRead();
     read_file();
     lock.unlock();
     ...
 }

 void WriterThread::run()
 {
     ...
     lock.lockForWrite();
     write_file();
     lock.unlock();
     ...
 }

void lockForRead():锁定读取锁。如果另一个线程已锁定以进行写入,则此函数将阻塞当前线程。如果线程已经锁定写入,则无法锁定读取。void lockForWrite():锁定写入锁。如果另一个线程(包括当前线程)已锁定读取或写入,则此函数将阻塞当前线程。如果线程已经为读取而锁定,则不会为写入而锁定。

基于QWaitCondition的线程同步

QWaitCondition 允许线程在某些情况发生时唤醒另外的线程。一个或多个线程可以阻塞等待一QWaitCondition ,用wakeOne()或wakeAll()设置一个条件。wakeOne()随机唤醒一个,wakeAll()唤醒所有

const int DataSize = 100000;
const int BufferSize = 8192;
char buffer[BufferSize];
QWaitCondition bufferNotEmpty;
QWaitCondition bufferNotFull;
QMutex mutex;
int numUsedBytes = 0;
class Producer : public QThread
{
public:
     void run();
};
void Producer::run()
{
     qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
     for (int i = 0; i < DataSize; ++i) {
         mutex.lock();
         if (numUsedBytes == BufferSize)
             bufferNotFull.wait(&mutex);
         mutex.unlock();
         buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];
         mutex.lock();
         ++numUsedBytes;
         bufferNotEmpty.wakeAll();
         mutex.unlock();
     }
}
class Consumer : public QThread
{
public:
     void run();
};
void Consumer::run()
{
     for (int i = 0; i < DataSize; ++i) {
         mutex.lock();
         if (numUsedBytes == 0)
             bufferNotEmpty.wait(&mutex);
         mutex.unlock();
         fprintf(stderr, "%c", buffer[i % BufferSize]);
         mutex.lock();
         --numUsedBytes;
         bufferNotFull.wakeAll();
         mutex.unlock();
     }
     fprintf(stderr, "\n");
}
int main(int argc, char *argv[])
{
     QCoreApplication app(argc, argv);
     Producer producer;
     Consumer consumer;
     producer.start();
     consumer.start();
     producer.wait();
     consumer.wait();
     return 0;
}

到此这篇关于一文带你了解Qt多线程的实现方式的文章就介绍到这了,更多相关Qt多线程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用Visual Studio 2010/2013编译V8引擎步骤分享

    使用Visual Studio 2010/2013编译V8引擎步骤分享

    这篇文章主要介绍了使用Visual Studio 2013编译V8引擎步骤分享,需要的朋友可以参考下
    2015-08-08
  • C语言之实现字符串小写变大写的实例

    C语言之实现字符串小写变大写的实例

    这篇文章主要介绍了C语言之实现字符串小写变大写的实例的相关资料,需要的朋友可以参考下
    2017-05-05
  • VSCode C/C++多文件编译配置小结

    VSCode C/C++多文件编译配置小结

    本文主要介绍了VSCode C/C++多文件编译配置小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-08-08
  • C++实现显示MP3文件信息的方法

    C++实现显示MP3文件信息的方法

    这篇文章主要介绍了C++实现显示MP3文件信息的方法,可实现显示如作者、专辑等(libZPlay)信息的功能,需要的朋友可以参考下
    2015-06-06
  • 设计模式中的备忘录模式解析及相关C++实例应用

    设计模式中的备忘录模式解析及相关C++实例应用

    这篇文章主要介绍了设计模式中的备忘录模式解析及相关C++实例应用,备忘录模式也经常被用来在命令模式中维护可以撤销(Undo)操作的状态,需要的朋友可以参考下
    2016-03-03
  • C语言创建windows窗口实例

    C语言创建windows窗口实例

    这篇文章主要介绍了C语言创建windows窗口实例,本文直接给出实现代码,同时讲解了编码的步骤,需要的朋友可以参考下
    2015-04-04
  • C++中指针函数与函数指针的使用

    C++中指针函数与函数指针的使用

    今天小编就为大家分享一篇关于C++中指针函数与函数指针的使用,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • C语言强制类型转换规则实例详解

    C语言强制类型转换规则实例详解

    强制类型转换是把变量从一种类型转换为另一种数据类型,下面这篇文章主要给大家介绍了关于C语言强制类型转换的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-06-06
  • C语言中的各种文件读写方法小结

    C语言中的各种文件读写方法小结

    这篇文章主要介绍了C语言中的各种文件读写方法小结,是C语言入门学习中的基础知识,需要的朋友可以参考下
    2015-07-07
  • C语言中printf的两种输出对齐方式

    C语言中printf的两种输出对齐方式

    C语言中左对齐是C语言的默认输出方式,右对齐是一种特殊的输出方式,左对齐和右对齐都对应着一个已知的输出宽度,输出的字符串根据字符串的长度在宽度上进行补充,补充字符是空格,在使用printf函数输出时,需要在格式字符串中使用%-*s和%*s的格式来分别表示
    2024-02-02

最新评论