
QtConcurrent 实际是一个命名空间,该命名空间提供了高级API,从而可以在不使用低级线程原语(启动线程、线程间同步、锁等)的情况下编写多线程程序。
但是QtConcurrent仅支持接受纯函数或者lambda表达式,不支持信号和槽,如果需要监听任务执行结果可以通过与QFuture和QFutureWatcher配合来达到。
QFuture 类表示异步计算的结果,使用Qt Concurrent框架中的API启用。
要使用信号和插槽与正在运行的任务进行交互,则需要使用QFutureWatcher。
QFutureWatcher 类允许使用信号和插槽监视QFuture。
简单的例子:
通过运行结果可以发现,QtConccurent管理的线程实际是从线程池分配线程资源的,而绑定QFutureWatcher的槽是在主线程中执行的。
在需要单次执行且内部逻辑较简单的时候使用QtConccurrent+QFuture+QFutureWatcher是很方便的,可以减少很多编码工作量,而且在多cpu环境中,QtConccurent也会启用多核。
包含QThread头文件
创建一个对象指针 QThread myThread;
myThread = new QThread;
myThread->start();
同时需要在新建的QThread线程文件中的run函数里面添加你想要用的代码即可。
建议看看QT开发的书,例子很多。也可以看帮主文档的。
Qt同步线程的几种方法
一、QMutex类
QMutex类就像一把锁,在互斥量之前上锁(QMutex::lock()),然后在使用完互斥量之后解锁(QMutex::unlock())。比如下面的代码:
[cpp] view plain copy
void someMethod()
{
mutexlock();
qDebug()<<"Hello";
qDebug()<<"World";
mutexunlock();
}
class Thread1 : public QThread
{
protected:
void run()
{
someMethod();
}
};
class Thread2 : public QThread
{
protected:
void run()
{
someMethod();
}
};
如上面的代码,在函数someMethod里面有两条语句,如果有两个线程启动之后,这两个线程都将调用这个函数(run函数即为线程启动后执行的程序),则可能会出现的结果是Hello Hello World World。但是这并不是我们想要的,我们希望的是每个线程可以一次性执行完someMethod函数里面的代码。这个时候我们便可以在函数俩面给函数体加上锁,然后在结束的时候解锁。
这里需要注意的是,如果一个线程试图向一个已经被其它线程上锁了互斥量上锁的话,这个线程将被阻塞,直到这个互斥量被解锁。如果一个线程希望自己在试图对一个上锁了的互斥量进行访问的时候能够不被阻塞,可以将lock()函数替换为tryLock()函数,这个函数的效果是:如果线程正在试图访问的互斥量已经被上锁了,那么可以立即返回而不被阻塞。
二、QMutexLocker便利类
使用QMutex对互斥量进行加锁解锁比较繁琐,在一些复杂的函数或者抛出C++异常的函数中都非常容易发生错误。可以使用一个方便的QMutexLocker类来简化对互斥量的处理。首先,QMutexLocker类的构造函数接收一个QMutex对象作为参数并且上锁,然后在析构函数中自动对其进行解锁。如下代码:
[cpp] view plain copy
QMutex mutex;
void someMethod()
{
QMutexLocker locker(&mutex);
qDebug()<<"Hello";
qDebug()<<"World";
}
这里创建一个QMutexLocker类实例,在这个实例的构造函数中将对mutex对象进行加锁。然后在析构函数中自动对mutex进行解锁。解锁的工作不需要显示地调用unlock函数,而是根据QMutexLocker对象的作用域绑定在一起了。
三、QReadWriteLock类
前两种保护互斥量的方法比较绝对,其达到的效果是:不管我要对互斥量做些是什么,我都要一个人霸占着,即使我只是看看它,也不能让别人看。这会使得这个互斥量资源的使用率大大下降,造成资源等待等问题。于是,我们可以对线程对互斥量的 *** 作进行分类:读和写。有几种情况:1、如果我只是看看的话,你也可以看,大家看到的都是正确的;2、如果我要看这个数据,你是不能改的,不然我看到的就不知道是什么了;3、我在改的时候,你不能看的,我可能会让你看到不正确的了;4、我在改的时候,你当然不能改了。
因此,我们可以对QMutex锁进行升级,将其升级为QReadWriteLock,QMutex加锁的方法是lock(),而QReadWriteLock锁有两种锁法:设置为读锁(lockForRead())和写锁(lockForWrite())。代码如下:
QReadWriteLock lock;
void someMethod()
{
locklockForRead();
//locklockForWrite();
qDebug()<<"Hello";
qDebug()<<"World";
lockunlock();
}
于是可能有一下三种情况:1、一个线程试图对一个加了读锁的互斥量进行上读锁,允许;2、一个线程试图对一个加了读锁的互斥量进行上写锁,阻塞;3、一个线程试图对一个加了写锁的互斥量进行上读锁,阻塞;一个线程试图对一个加了写锁的互斥量进行上写锁,阻塞。
所以可以看出,读写锁比较适用的情况是:需要多次对共享的数据进行读 *** 作的阅读线程。
四、QReadLocker便利类和QWriteLocker便利类对QReadWriteLock进行加解锁
和QMutex与QMutexLocker类的关系类似,关于读写锁也有两个便利类,读锁和写锁,QReadLocker和QWriteLocker。它们的构造函数都是一个QReadWriteLock对象,不同的是,在QReadLocker的构造函数里面是对读写锁进行lockForRead()加锁 *** 作,而在QWriteLocker的构造函数里面是对读写锁进行lockForWrite()加锁 *** 作。然后解锁 *** 作unlock()都是在析构函数中完成的。
五、信号量QSemaphore
前面的几种锁都是用来保护只有一个量的互斥量的。但是还有些互斥量(资源)的数量并不止一个,比如一个电脑安装了2个打印机,我已经申请了一个,但是我不能霸占这两个,你来访问的时候如果发现还有空闲的仍然可以申请到的。于是这个互斥量可以分为两部分,已使用和未使用。一个线程在申请的时候,会对未使用到的部分进行加锁 *** 作,如果加锁失败则阻塞,如果加锁成功,即又有一个资源被使用了,于是则将已使用到的部分解锁一个。以著名的生产者消费者问题为例,分析问题:生产者需要的是空闲位置存放产品,结果是可取的产品多了一个。于是,我们可以定义两个信号量:QSemaphore freeSpace和QSemaphore usedSpace,前者是给生产者使用的,后者是给
六、条件触发QWaitCondition(未完待续、、、)
一个对象的线程就是创建该对象时的线程,而不论该对象的定义是保存在那个线程中;
子线程中定义的对象的情况:( 其实也好理解,子线程中,创建otherObj的时候,不再run()函数中,所以,otherObj对象不属于MyTHread )
1
2
3
4
5
6
7
8
9
10
11
12
13
classMyThread:publicQThread
{
public:
MyThread()
{
//因为该构造函数属于主线程,所以,otherObj依附于主线程,即属于主线程,而不属于子线程MyThread,这是关键
otherObj=newQObject;
}
private:
QObjectotherObj;
};
在我们创建了MyThread对象之后,otherObj的线程依附性是怎样的?是不是就是MyThread所表示的那个线程?
要回答这个问题,我们必须看看究竟是哪个线程创建了它们:实际上,是调用了MyThread构造函数的线程创建了它们。因此,这些对象不在MyThread所表示的线程,而是在创建了MyThread的那个线程中。即主线程,
我们可以通过调用QObject::thread()可以查询一个QObject的线程依附性。
以上就是关于Qt多线程:QtConcurrent + QFuture + QFutureWatcher全部的内容,包括:Qt多线程:QtConcurrent + QFuture + QFutureWatcher、QT线程如何使用的、Qt同步线程的几种方法等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)