线程
线程分为gui线程(主线程)和次要线程,涉及gui的必须在主线程
线程的同步
两个线程可以同时访问一个对象,试想如果一个对象同时执行两种方法则可能出错
QMutex 是强制执行互斥的基本类。一个线程锁定一个互斥量(mutex),以获得共享资源的访问权限。如果另一个线程试图锁定互斥量,而互斥量已被锁定,这时,它将进入睡眠状态,直到第一个线程完成其任务并解锁互斥量。
QReadWriteLock 类似于 QMutex,除了它区分了“读”和“写”的访问。当一个数据块没有被写入,多线程对他同时进行读取是安全的。一个 QMutex 迫使轮流读取共享数据,而 QReadWriteLock 允许同时读取,从而提高并行性。
QSemaphore 是 QMutex 的一个推广,可以保护一定数量相同的资源。相比之下,一个 QMutex 只能保护一个资源
QWaitCondition 同步线程,不通过执行互斥,而通过提供一个条件变量。其它原语使线程等待,直到资源被解锁
QMutexLocker、QReadLocker 和 QWriteLocker 是便利类,使其更易于使用 QMutex 和QReadWriteLock 。当他们被构建时,就会锁定资源;当被销毁时,就会自动解锁。设计他们是为了简化使用 QMutex 和 QReadWriteLock 的代码,从而减少资源被永久锁定的可能性。
线程安全的可重入类
注意点:
1.事件的驱动对象只能被使用在单线程中,也就是不能在此线程中引用别的线程的socket或者定时器之类的
2.gui类是不可重入的,只能放在主线程中
class Counter
{
public:
Counter() { n = 0; }
void increment() { QMutexLocker locker(&mutex); ++n; }//局部变量locker销毁时mutex被释放相当于unlock了
void decrement() { QMutexLocker locker(&mutex); --n; }
int value() const { QMutexLocker locker(&mutex); return n; }
private:
mutable QMutex mutex;//由于const,所以加mutable,可修改
int n;
};
**QObject::moveToThread()**函数改变一个对象和它的孩子的线程所属性。
从另一个线程(不是该QObject对象所属的线程)对该QObject对象调用delete方法是不安全的,除非你能保证该对象在那个时刻不处理事件,使用QObejct::deleteLater()更好。一个DeferredDelete类型的事件将被提交(posted),而该对象的线程的事件循环最终会处理这个事件。默认情况下,拥有一个QObject的线程就是创建QObject的那个线程,而不是QObject::moveToThread()被调用后的。
QCoreApplication::postEvent(),你可以在任何时刻给任何线程中的任何对象发送事件,这些事件将自动被分发到该对象所被创建的线程事件循环中。
**QCoreApplication::sendEvent()**不同于postEvent())只能将事件分发到和该函数调用者相同的线程中的对象。
可以在QThread::run()的实现中安全地发射信号,因为信号发射是线程安全的或者必须用一个mutex保护对该QObject子类的内部数据的所有访问
QtConcurrent
该QtConcurrent命名空间提供高层次的API,使人们可以编写多线程程序,而无需使用低级线程原语。
Header: #include
qmake: QT += concurrent
/*
*QStringList lowerCaseStrings = QtConcurrent::blockingFiltered(strlist, allLowerCase);//返回序列
* QtConcurrent::blockingFilter(strlist, allLowerCase);//直接修改
*/
bool allLowerCase(const QString &str)
{
return str.toLower()==str;
}
void addToDictionary(QString &dictionary, const QString &string)
{
dictionary.append(string);
}
GuiTest::GuiTest(QWidget *parent)
: QMainWindow(parent)
{
QStringList strlist;
strlist<<"zylg1"<<"zylg2"<<"123"<<"ZYLG"<<"Zylg";
foreach(QString str ,strlist)cout<<str<<"\t";cout<<endl;
QStringList lowerCaseStrings = QtConcurrent::blockingFiltered(strlist, allLowerCase);//返回序列
QFuture<QString> dictionary = QtConcurrent::filteredReduced(strlist, allLowerCase, addToDictionary);
QtConcurrent::blockingFilter(strlist, allLowerCase);//直接修改
foreach(QString str ,strlist)cout<<str<<"\t";cout<<endl;
foreach(QString str ,lowerCaseStrings)cout<<str<<"\t";cout<<endl;
foreach(QString str ,dictionary)cout<<str<<"\t";cout<<endl;
}
/*
* result:
* zylg1 zylg2 123 ZYLG Zylg
* zylg1 zylg2 123
* zylg1 zylg2 123
* zylg1zylg2123
*/
QImage scaled(const QImage &image)
{
return image.scaled(100, 100);
}
QList<QImage> images = ...;
QFuture<QImage> thumbnails = QtConcurrent::mapped(images, scaled);
//void QtConcurrent::blockingMap(Iterator begin, Iterator end, MapFunctor function)
//QList<int> ints = QtConcurrent::blockingMapped<QList<int> >(beginIterator, endIterator, fn);
#include<QTextStream>
#include<QtConcurrent>
#include<QString>
#include<QSet>
#include<QFuture>
#include<iterator>
#include<QFile>
#include<QDir>
#include<QList>
#include<QTime>
QTextStream cout(stdout);
//递归搜索文件
QStringList findFiles(const QString &startDir, QStringList filters)
{
QStringList names;
QDir dir(startDir);
foreach (QString file, dir.entryList(filters, QDir::Files))
names += startDir + '/' + file;
foreach (QString subdir, dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot))
names += findFiles(startDir + '/' + subdir, filters);
return names;
}
// 单线程统计文件大小
qint64 singleThreadedSize(QStringList files)
{
qint64 folder_size=0;
foreach (QString file, files) {
QFile f(file);
folder_size+=f.size();
}
return folder_size;
}
//计算单个文件大小,用于线程并发
qint64 fileSize(const QString &file)
{
QFile f(file);
return f.size();
}
//统计总的大小,用于线程并发结果的处理
void sumSize(qint64 &sumsize,const qint64 &size)
{
sumsize+=size;
}
GuiTest::GuiTest(QWidget *parent)
: QMainWindow(parent)
{
qDebug()<<"find files ...";
QStringList file_list=findFiles("/home/zz",QStringList()<<"*.*");
// foreach (QString str, file_list) {
// cout<<str<<endl;
// }
int single_thread_time=0;
{
QTime time;
time.start();
qint64 folder_size=singleThreadedSize(file_list);
single_thread_time=time.elapsed();
cout<<"singke Time is :"<<single_thread_time<<endl<<"size is:"<<folder_size/1024/1024<<"M"<<endl;
}
int concurrent_thread_time=0;
{
QTime time;
time.start();
qint64 folder_size2=QtConcurrent::mappedReduced(file_list,fileSize,sumSize);
concurrent_thread_time=time.elapsed();
cout<<"concurrut Time is :"<<concurrent_thread_time<<endl<<"size is:"<<folder_size2/1024/1024<<"M"<<endl;
}
qDebug()<<(double)single_thread_time/concurrent_thread_time;
}
/*
* result:
* find files ...
* singke Time is :76
* size is:1927M
* concurrut Time is :34
* size is:1927M
* 2.23529
*/
QtConcurrent::run() 函数在一个单独的线程中运行一个函数, 函数的返回值通过 QFuture API 提供。
extern void aFunction();
QFuture<void> future = QtConcurrent::run(aFunction);
QTextStream cout(stdout);
//递归搜索文件
QStringList findFiles(const QString &startDir, QStringList filters)
{
QStringList names;
QDir dir(startDir);
foreach (QString file, dir.entryList(filters, QDir::Files))
names += startDir + '/' + file;
foreach (QString subdir, dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot))
names += findFiles(startDir + '/' + subdir, filters);
return names;
}
// 单线程统计文件大小
qint64 singleThreadedSize(QStringList files)
{
qint64 folder_size=0;
foreach (QString file, files) {
QFile f(file);
folder_size+=f.size();
}
return folder_size;
}
GuiTest::GuiTest(QWidget *parent)
: QMainWindow(parent)
{
QStringList filter;
filter<<"*.*";
QString dir("/home/zz");
QFuture<QStringList> strlistfu=QtConcurrent::run(findFiles,dir,filter);
QStringList strlist=strlistfu.result();
QFuture<qint64> sizefu=QtConcurrent::run(singleThreadedSize,strlist);//启动一个线程
//QFuture 获取一步计算的结果,有个迭代器
qint64 size=sizefu.result()/1024/1024;
QTextBrowser *texbrowser=new QTextBrowser;
texbrowser->append(QString("%0M").arg(size));
texbrowser->resize(500,500);
texbrowser->show();
}
/*
*1927M
*/
QFutureWatcher监视QFuture
函数 | 作用 |
---|---|
QFuture future() const | |
bool isCanceled() const | |
bool isFinished() const | |
bool isPaused() const | |
bool isRunning() const | |
bool isStarted() const | |
int progressMaximum() const | |
int progressMinimum() const | |
QString progressText() const | |
int progressValue() const | |
T result() const | |
T resultAt(int index) const | |
void setFuture(const QFuture &future) | |
void setPendingResultsLimit(int limit) | |
void waitForFinished() |
slots:
void cancel()
void pause()
void resume()
void setPaused(bool paused)
void togglePaused()
signals:
void canceled()
void finished()
void paused()
void progressRangeChanged(int minimum, int maximum)
void progressTextChanged(const QString &progressText)
void progressValueChanged(int progressValue)
void resultReadyAt(int index)
void resultsReadyAt(int beginIndex, int endIndex)
void resumed()
void started()
// 实例化对象,并连接到 finished() 信号。
MyClass myObject;
QFutureWatcher<int> watcher;
connect(&watcher, SIGNAL(finished()), &myObject, SLOT(handleFinished()));
// 开始计算
QFuture<int> future = QtConcurrent::run(...);
watcher.setFuture(future);
// 连接信号槽 - 加载、显示进度、打开、取消等操作
connect(m_pWatcher, SIGNAL(resultReadyAt(int)), SLOT(showImage(int)));
connect(m_pWatcher, SIGNAL(progressRangeChanged(int,int)), pProgressBar, SLOT(setRange(int,int)));
connect(m_pWatcher, SIGNAL(progressValueChanged(int)), pProgressBar, SLOT(setValue(int)));
connect(m_pWatcher, SIGNAL(finished()), SLOT(finished()));
QThread
函数 | 作用 |
---|---|
void exit(int returnCode = 0) | 退出 |
bool isFinished() const | 是否完成 |
bool isInterruptionRequested() const | |
bool isRunning() const | 是否运行 |
int loopLevel() const | |
Priority priority() const | 优先级 |
void requestInterruption() | |
void setEventDispatcher(QAbstractEventDispatcher *eventDispatcher) | |
void setPriority(Priority priority) | 优先级 |
void setStackSize(uint stackSize) | |
uint stackSize() const | |
bool wait(unsigned long time = ULONG_MAX) | |
QThread * currentThread() | 目前线程 |
Qt::HANDLE currentThreadId() | |
int idealThreadCount() | |
void msleep(unsigned long msecs) | |
void sleep(unsigned long secs) |
slots:
quit()
start(Priority priority = InheritPriority)
terminate()
signals:
void finished()
void started()
protected function:
int exec()
virtual void run()
class WorkerThread : public QThread
{
Q_OBJECT
void run() override {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &s);
};
void MyObject::startWorkInAThread()
{
WorkerThread *workerThread = new WorkerThread(this);
connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
workerThread->start();
}
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include<QThread>
#include<QMutexLocker>
#include<QDebug>
class MyThread:public QThread
{
Q_OBJECT
public:
MyThread(QObject *parent=0):QThread(parent)
{
qDebug()<<"thread is created";
m_bStopped=false;
}
~MyThread()
{
stop();
quit();
wait();
}
void stop()
{
qDebug()<<"thread stop:"<<QThread::currentThreadId();
QMutexLocker locker(&m_mutex);
m_bStopped=true;
}
void run()
{
m_bStopped=false;
qDebug()<<"thread runing"<<QThread::currentThreadId();
int value=0;
while(value<100)
{
msleep(50);//休眠50毫秒
++value;
qDebug()<<value;
emit resultReady(value);
{
QMutexLocker locker(&m_mutex);
if(m_bStopped)break;
}
if(value>=100 && m_bStopped==false)value=0;
if(m_bStopped==true)break;
}
}
private:
bool m_bStopped;
QMutex m_mutex;
signals:
void resultReady(int);
public slots:
void setPause()
{
m_bStopped=true;
}
};
#endif // MYTHREAD_H
QTextStream cout(stdout);
GuiTest::GuiTest(QWidget *parent)
: QMainWindow(parent)
{
QWidget *wid=new QWidget();
this->setCentralWidget(wid);
QVBoxLayout *main_layout=new QVBoxLayout(wid);
QHBoxLayout *layout=new QHBoxLayout();
QPushButton *start_bt=new QPushButton("start",wid);
QPushButton *canel_bt=new QPushButton("canel",wid);
layout->addStretch();
layout->addWidget(start_bt);
layout->addStretch();
layout->addWidget(canel_bt);
layout->addStretch();
QProgressBar *m_probar=new QProgressBar(wid);
m_probar->setRange(0,100);
m_probar->setValue(0);
main_layout->setContentsMargins(10,20,20,20);
main_layout->addWidget(m_probar);
main_layout->setSpacing(20);
main_layout->addLayout(layout);
this->resize(500,400);
//=============上面为布局,现在开始调用线程,控制m_probar的value========
connect(start_bt,QOverload<bool>::of(&QPushButton::clicked),[m_probar,canel_bt]{
MyThread *thread=new MyThread();
connect(canel_bt,SIGNAL(clicked(bool)),thread,SLOT(setPause()));
connect(thread,QOverload<int>::of(&MyThread::resultReady),[m_probar](int val){
m_probar->setValue(val);
});
thread->start();
});
this->show();
}