QThread源码浅析[通俗易懂]

2022-09-02 10:19:56 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

Qt版本 Qt5.6.0,下面以Windows平台为例简单研究下QThread源码实现。

1.仅研究下QThread::start()函数,其他细节在次不涉及:

srcqtbasesrccorelibthreadqthread_win.cpp

代码语言:javascript复制
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");
    }
}

2.挑出里面的重点:

代码语言:javascript复制
#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

3.看到了Windows下创建线程的函数 _beginthreadex,来看下这个函数的几个参数:

代码语言:javascript复制
unsigned long _beginthreadex( 

void *security,       // 安全属性,NULL为默认安全属性

unsigned stack_size,  // 指定线程堆栈的大小。如果为0,则线程堆栈大小和创建它的线程的相同。一般用0

unsigned ( __stdcall *start_address )( void * ), 
                      // 指定线程函数的地址,也就是线程调用执行的函数地址(用函数名称即可,函数名称就表示地址)

void *arglist,        // 传递给线程的参数的指针,可以通过传入对象的指针,在线程函数中再转化为对应类的指针

unsigned initflag,    // 线程初始状态,0:立即运行;CREATE_SUSPEND:suspended(悬挂)

unsigned *thrdaddr    // 用于记录线程ID的地址

);

4.参数 start_address 和 arglist 是线程创建的重点,对应QThread::start中的代码看下:

start_address — QThreadPrivate::start

arglist — this 指针,也就是QThread或者其派生类对象本身

5.接下来看下QThreadPrivate::start :

srcqtbasesrccorelibthreadqthread_win.cpp

代码语言:javascript复制
// 参数arg其实为QThread对象的this指针
unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(void *arg)
{
    QThread *thr = reinterpret_cast<QThread *>(arg);
    QThreadData *data = QThreadData::get2(thr);

    // 创建线程局部存储变量,存放线程id
    qt_create_tls();
    TlsSetValue(qt_current_thread_data_tls_index, data);
    data->threadId = reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId()));

    QThread::setTerminationEnabled(false);

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

    if (data->eventDispatcher.load()) // custom event dispatcher set?
        data->eventDispatcher.load()->startingUp();
    else
        createEventDispatcher(data);
    ... ...
    emit thr->started(QThread::QPrivateSignal()); // 发射线程启动信号
    QThread::setTerminationEnabled(true);
    thr->run(); // 调用QThread::run()虚函数 -- 线程函数

    finish(arg);
    return 0;
}

终于看到QThread::run虚函数是在哪儿调用的,也看到了QThread::started信号是在哪儿发射的!

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/140342.html原文链接:https://javaforall.cn

0 人点赞