如何启动和终止线程的思考

QT提供了QThread类来实现多线程编程。QThread::terminate()用来结束子线程。假如调用terminate()后,子线程不是立刻停止,程序员还可以调用QThread::wait()等待子线程结束,才进行后面的操作。在以前的项目里,我经常使用terminate + wait的组合来结束线程。但是前几天发现,这样的组合并非总能成功:子线程有时不能及时结束,造成主线程也卡在wait()处,整个程序不能运行。下面的小程序展示的是一个terminate + wait的例子。当然,这个程序是可以及时终止的,不会有子线程残留。这个程序只是展示terminate + wait的用法:

main.cpp

#include "mainwindow.h"
#include <QApplication>
#include "thrd.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    thrd t;
    t.start();
    MainWindow w;
    w.show();

    return a.exec();
}

thrd.cpp

#include "thrd.h"
#include <QDebug>

thrd * g_pThrd = NULL;
thrd::thrd(QObject *parent) : QThread(parent)
{
    g_pThrd = this;
}

void thrd::run()
{
    while(true)
    {
        qDebug()<<"hello";
        msleep(1000);
    }
}

thrd * thrd::pGetInstance()
{
    return g_pThrd;
}

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "thrd.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    thrd * p = thrd::pGetInstance();
    p->terminate();
    p->wait();
    QMainWindow::closeEvent(event);
}

回到那个被子线程卡住的程序上来。经断点调试,发现子线程不能被及时终止,而是卡在了wait处。感觉是qt的terminate函数有时不好用。以前在MFC编程时,我的师傅也遇到过类似的问题。解决的办法时,不要在子线程里用while(true)这样的死循环。而是采用 while(bool)这种方式。主线程关闭子线程时,只要把布尔变量变为false即可。下面的例子就是这样的示范:

thrd.cpp

#include "thrd.h"
#include <QDebug>

Thrd * g_pThrd = NULL;

Thrd::Thrd(QObject *parent) : QThread(parent)
{
    g_pThrd = this;
    m_bPermission = false;
    m_bRunning = false;
}

void Thrd::run()
{
    m_bRunning = true;
    while(m_bPermission)
    {
        qDebug()<<1;
        msleep(1000);
    }

    qDebug()<<"finish";
    m_bRunning = false;
}

void Thrd::vSetPermission(bool b)
{
    m_bPermission = b;
}

void Thrd::vStop()
{
    while(m_bRunning){}
}

Thrd * Thrd::pGetInstance()
{
    return g_pThrd;
}

main.cpp

#include "mainwindow.h"
#include <QApplication>
#include "thrd.h"


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    Thrd thd;
    thd.vSetPermission(true);
    thd.m_bRunning = true;
    thd.start();

    MainWindow w;
    w.show();

    return a.exec();
}

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "thrd.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    Thrd * pThrd = Thrd::pGetInstance();
    if(pThrd)
    {
        pThrd->vSetPermission(false);
        pThrd->vStop();
    }
    QMainWindow::closeEvent(event);
}

在问题项目里采用了以上策略后,程序不再卡死。

有人可能会认为main.cpp中 thd.m_bRunning = true语句没必要,因为在run()函数一开始也赋值了m_bRunning

但是不要忘记,在微观尺度上,线程的调度很难保证均匀:主线程可能已经运行了几十条指令,但是子线程可能一步都没走;或者反之。假如没有main.cpp中 thd.m_bRunning = true一句,直接使用start()启动线程,那么子线程可能不会立刻启动,于是m_bRunning始终保持在false值。假如操作员“很快的”在程序启动后立刻关闭窗体,那么有这种可能--窗体关闭时,因为m_bRunning还是false,所以while(m_bRunning)立刻就过去了。于是,子线程在while(m_bRunning)之后才启动。假如程序员考虑不到这种情况,出于某种需要,在while(m_bRunning)之后又把m_bPemission设置为true,则子线程又有可能跑起来,而且程序员还蒙在鼓里。

扫描二维码关注公众号,回复: 9196579 查看本文章
发布了148 篇原创文章 · 获赞 46 · 访问量 28万+

猜你喜欢

转载自blog.csdn.net/liji_digital/article/details/88068827