【QT】 QThread部分源码浅析

本文章挑出QThread源码中部分重点代码来说明QThread启动到结束的过程是怎么调度的。其次因为到了Qt4.4版本,Qt的多线程就有所变化,所以本章会以Qt4.0.1和Qt5.6.2版本的源码来进行浅析。

QThread类的定义源码

Qt4.0.1版本源码:

#ifndef QT_NO_THREAD class Q_CORE_EXPORT QThread : public QObject { public: ...//省略 explicit QThread(QObject *parent = 0); ~QThread(); ...//省略 void exit(int retcode = 0); public slots: void start(QThread::Priority = InheritPriority); //启动线程函数 void terminate(); //强制退出线程函数 void quit(); //线程退出函数 ...//省略 signals: void started(); //线程启动信号 void finished(); //线程结束信号 ...//省略 protected: virtual void run() = 0; int exec(); ...//省略 }; #else // QT_NO_THREAD

Qt5.6.2版本源码:

#ifndef QT_NO_THREAD class Q_CORE_EXPORT QThread : public QObject { Q_OBJECT public: ...//省略 explicit QThread(QObject *parent = Q_NULLPTR); ~QThread(); ...//省略 void exit(int retcode = 0); //线程退出函数 ...//省略 public Q_SLOTS: void start(Priority = InheritPriority); //启动线程函数 void terminate(); //强制退出线程函数 void quit(); //线程退出函数 ...//省略 Q_SIGNALS: void started(QPrivateSignal); //线程启动信号 void finished(QPrivateSignal); //线程结束信号 protected: virtual void run(); int exec(); ...//省略 }; #else // QT_NO_THREAD

从以上两个版本的代码可以看出,这些函数在声明上基本没什么差异,但是仔细看,两个版本的 run() 函数声明的是不是不一样?

Qt4.0.1版本run() 函数是纯虚函数,即此类为抽象类不可以创建实例,只可以创建指向该类的指针,也就是说如果你需要使用QThread来实现多线程,就必须实现QThread的派生类并且实现 run() 函数;

Qt5.6.2版本的run() 函数是虚函数,继承QThread类时,可以重新实现 run() 函数,也可以不实现。

注:我查看了多个Qt版本的源码,发现出现以上差异的版本是从Qt4.4开始的。从Qt4.4版本开始,QThread类就不再是抽象类了。

QThread::start()源码

再来看看QThread::start()源码,Qt4.0.1版本和Qt5.6.2版本此部分的源码大同小异,所以以Qt5.6.2版本的源码为主,如下:

void QThread::start(Priority priority) { Q_D(QThread); QMutexLocker locker(&d->mutex); if (d->isInFinish) { locker.unlock(); wait(); locker.relock(); } if (d->running) return; ... ... // 此部分是d指针配置 #ifndef Q_OS_WINRT ... ... // 此部分为注释 d->handle = (Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start, this, CREATE_SUSPENDED, &(d->id)); #else // !Q_OS_WINRT d->handle = (Qt::HANDLE) CreateThread(NULL, d->stackSize, (LPTHREAD_START_ROUTINE)QThreadPrivate::start, this, CREATE_SUSPENDED, reinterpret_cast<LPDWORD>(&d->id)); #endif // Q_OS_WINRT if (!d->handle) { qErrnoWarning(errno, "QThread::start: Failed to create thread"); d->running = false; d->finished = true; return; } int prio; d->priority = priority; switch (d->priority) { ... ... // 此部分为线程优先级配置 case InheritPriority: default: prio = GetThreadPriority(GetCurrentThread()); break; } if (!SetThreadPriority(d->handle, prio)) { qErrnoWarning("QThread::start: Failed to set thread priority"); } if (ResumeThread(d->handle) == (DWORD) -1) { qErrnoWarning("QThread::start: Failed to resume new thread"); } }

挑出里面的重点来说明:

(1)Q_D()宏定义

在看源码的时候,当时比较好奇start函数的第一条语句 Q_D()宏定义 是什么意思,所以就看了下源码,在此也顺便讲讲,Q_D() 源码是一个宏定义,如下:

#define Q_D(Class) Class##Private * const d = d_func()

此处利用了预处理宏里的 ## 操作符:连接前后两个符号,变成一个新的符号。将Q_D(QThread)展开后,变成:QThreadPrivate * const d = d_func()。

(2)_beginthreadex()函数
上面d->handle = (Qt::HANDLE) _beginthreadex ( NULL, d->stackSize, QThreadPrivate::start, this, CREATE_SUSPENDED, &( d->id ) ) 语句中的函数是创建线程的函数,其原型以及各参数的说明如下:

unsigned long _beginthreadex( void *security, // 安全属性,NULL为默认安全属性 unsigned stack_size, // 指定线程堆栈的大小。如果为0,则线程堆栈大小和创建它的线程的相同。一般用0 unsigned ( __stdcall *start_address )( void * ), // 指定线程函数的地址,也就是线程调用执行的函数地址(用函数名称即可,函数名称就表示地址) void *arglist, // 传递给线程的参数的指针,可以通过传入对象的指针,在线程函数中再转化为对应类的指针 //如果传入this,这个this表示调用QThread::start的对象地址,也就是QThread或者其派生类对象本身 unsigned initflag, // 线程初始状态,0:立即运行;CREATE_SUSPEND:suspended(悬挂) unsigned *thrdaddr // 用于记录线程ID的地址 ); QThreadPrivate::start()源码

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpyzzz.html