Qt源码解析之QThread

Qt源码解析之QThread

一、用法简介

1.工作类

class Worker : public QObject
{
    Q_OBJECT
public:
	explicit Worker(QObject *parent = nullptr);
	~Worker();
public slots:
	void doWork(void);
};
Worker::Worker(QObject *parent) : QObject(parent)
{

}
Worker::~Worker()
{

}

void Worker::doWork()
{
	qDebug() << "current thread id: " << QThread::currentThreadId();
}

2.控制类

class Controller : public QObject
{
    Q_OBJECT
public:
   	explicit Controller(QObject *parent = nullptr);
   	~Controller();
   	//启动工作线程
   	void start();
private:
	Worker *m_worker;
	QThread *m_thread;
};

Controller::Controller(QObject *parent) : QObject(parent)
{
	m_worker = new Worker;
	m_thread = new QThread(this);
	m_worker->moveToThread(m_thread);
	connect(m_thread,&QThread::finished,m_worker,&Worker::deleteLater);
	connect(m_thread,&QThread::started,m_worker,&Worker::doWork);
}

Controller::~Controller()
{
	
}
void Controller::start()
{
	m_thread->start();
}
  1. 使用
int main(int argc,char *argv[])
{
	QApplication a(argc, argv);
	Controller controller;
	controller.start();
	return a.exec();
}

二、源码分享

1.Qt官方推荐使用类似上面工作类的方式,来使用线程,将耗时任务放在Worker的槽函数中,使用QObject::moveToThread()将Worker类的线程亲和性转移到工作线程中,然后通过connet线程类QThrad的started()信号到doWork(),在上述示例中,作为线程管理类QThread的实例m_thread参与的是主线程的事件循环,工作类m_worker在执行moveToThread()后,参与的是m_thread所管理的新工作线程的事件循环,执行connect(m_thread,&QThread::started,m_worker,&Worker::doWork);时,使用的连接方式自动转换为Qt::QueuedConnection,也就是说doWork槽函数会在新的工作线程中执行。
2.首先看一下QThread的构造函数

//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\thread\qthread.cpp:414
QThread::QThread(QObject *parent)
    : QObject(*(new QThreadPrivate), parent)//在构造线程数据类QThreadPrivate时,使用的是默认参数nullptr,
{
    Q_D(QThread);
    // fprintf(stderr, "QThreadData %p created for thread %p\n", d->data, this);
    d->data->thread.storeRelaxed(this);//此处在QThreadData中记录线程指针,隶属于本线程的QObject对象在调用
    //QObject::thread()接口时,返回的就是该指针
}

QThreadPrivate::QThreadPrivate(QThreadData *d)
    : QObjectPrivate(), running(false), finished(false),
      isInFinish(false), interruptionRequested(false),
      exited(false), returnCode(-1),
      stackSize(0), priority(QThread::InheritPriority), data(d)
{

// INTEGRITY doesn't support self-extending stack. The default stack size for
// a pthread on INTEGRITY is too small so we have to increase the default size
// to 128K.
#ifdef Q_OS_INTEGRITY
    stackSize = 128 * 1024;
#elif defined(Q_OS_RTEMS)
    static bool envStackSizeOk = false;
    static const int envStackSize = qEnvironmentVariableIntValue("QT_DEFAULT_THREAD_STACK_SIZE", &envStackSizeOk);
    if (envStackSizeOk)
        stackSize = envStackSize;
#endif

#if defined (Q_OS_WIN)
    handle = 0;
    id = 0;
    waiters = 0;
    terminationEnabled = true;
    terminatePending = false;
#endif

    if (!data)//通过接口类QThread构造函数知,传入的data为nullptr,执行if分支语句
        data = new QThreadData;//新建一个线程时,首先构造一个线程数据类,用来持有线程相关的事件队列、事件分发句柄、
        //事件循环列表等整个线程共享的数据

3.QThread是线程管理类,负责新线程的创建、杀死等线程管理工作,他同样是QObject的子类,也具备QObject所共有的线程亲和性,比如在上述示例中,我们是在ui主线程中构造的QThread类,m_thread实例就属于主线程,参与主线程的事件循环,m_thread对象的成员变量(像m_worker这种通过moveToThread()改变了线程亲和性的除外)也属于主线程,在m_thread的成员函数和m_worker的槽函数中访问m_thread的成员变量时,要注意多线程访问临界区的同步问题,通过在我们执行QThread::start()时,会真正调用平台相关的线程api来正式创建一个线程

//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\thread\qthread_win.cpp:432
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->running = true;
    d->finished = false;
    d->exited = false;
    d->returnCode = 0;
    d->interruptionRequested = false;

    /*
      NOTE: we create the thread in the suspended state, set the
      priority and then resume the thread.

      since threads are created with normal priority by default, we
      could get into a case where a thread (with priority less than
      NormalPriority) tries to create a new thread (also with priority
      less than NormalPriority), but the newly created thread preempts
      its 'parent' and runs at normal priority.
    */
#if defined(Q_CC_MSVC) && !defined(_DLL)
    // MSVC -MT or -MTd build
    d->handle = (Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start,
                                            this, CREATE_SUSPENDED, &(d->id));
#else
    // MSVC -MD or -MDd or MinGW build 
    //此处调用win32 API创建线程,可以看出,线程的真正入口为QThreadPrivate::start(),参数为this指针
    d->handle = CreateThread(nullptr, d->stackSize,
                             reinterpret_cast<LPTHREAD_START_ROUTINE>(QThreadPrivate::start),
                             this, CREATE_SUSPENDED, reinterpret_cast<LPDWORD>(&d->id));
#endif

    if (!d->handle) {
        qErrnoWarning("QThread::start: Failed to create thread");
        d->running = false;
        d->finished = true;
        return;
    }

    int prio;
    d->priority = priority;
    switch (d->priority) {
    case IdlePriority:
        prio = THREAD_PRIORITY_IDLE;
        break;

    case LowestPriority:
        prio = THREAD_PRIORITY_LOWEST;
        break;

    case LowPriority:
        prio = THREAD_PRIORITY_BELOW_NORMAL;
        break;

    case NormalPriority:
        prio = THREAD_PRIORITY_NORMAL;
        break;

    case HighPriority:
        prio = THREAD_PRIORITY_ABOVE_NORMAL;
        break;

    case HighestPriority:
        prio = THREAD_PRIORITY_HIGHEST;
        break;

    case TimeCriticalPriority:
        prio = THREAD_PRIORITY_TIME_CRITICAL;
        break;

    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");
    }
}

4.让我们的视角切换到线程入口

//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\thread\qthread_win.cpp:298
unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(void *arg) noexcept
{
    QThread *thr = reinterpret_cast<QThread *>(arg);//CreateThread()中,传入的this指针
    QThreadData *data = QThreadData::get2(thr);//获取了本线程持有的线程数据

    qt_create_tls();
    TlsSetValue(qt_current_thread_data_tls_index, data);
    data->threadId.storeRelaxed(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));

    QThread::setTerminationEnabled(false);

    {
        QMutexLocker locker(&thr->d_func()->mutex);
        data->quitNow = thr->d_func()->exited;
    }

    data->ensureEventDispatcher();//确认是否为该线程分配了事件分发句柄,如果没有,就抓紧分配一个
    data->eventDispatcher.loadRelaxed()->startingUp();

#if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC)
    // sets the name of the current thread.

    // avoid interacting with the binding system while thread is
    // not properly running yet
    auto priv = QObjectPrivate::get(thr);
    QByteArray objectName = (priv->extraData ? priv->extraData->objectName.valueBypassingBindings()
                                         : QString()).toLocal8Bit();
    qt_set_thread_name(HANDLE(-1),
                       objectName.isEmpty() ?
                       thr->metaObject()->className() : objectName.constData());
#endif

    emit thr->started(QThread::QPrivateSignal());//此时发出的started信号,正是我们经常关联的那个
    QThread::setTerminationEnabled(true);
    thr->run();//调用了run虚函数,里面默认调用了Qt的事件循环,作为该线程的最底层的事件循环,事件系统会单开一篇,这里就不展开了

    finish(arg);
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值