简体   繁体   中英

QThread::exit or QThread::quit won't terminate the thread created by derived class

I thought that is a quite simple problem. But after tried some times, I realize I have spent more time than expected. So I come here for your help.

My environment

ubuntu 18.04
qt 5.15.0
cmake 3.18.1  # don't think it matters

I have minimize my program into a mini program. It's simple. There are two buttons, one start button and one stop button for starting and stopping thread created by instance of SubThread which is derived from QThread. I uploaded my codes here in minimal program .

To reproduce that problem, start up miniprogram from QCreator and then push the start button. The SubThread thread prints messages as expected. But then push the stop button, that thread will not exit which is unexpected. I'm stopped here.

CMakeLists.txt

cmake_minimum_required(VERSION 3.5)

project(miniprogram LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# QtCreator supports the following variables for Android, which are identical to qmake Android variables.
# Check http://doc.qt.io/qt-5/deployment-android.html for more information.
# They need to be set before the find_package(Qt5 ...) call.

#if(ANDROID)
#    set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
#    if (ANDROID_ABI STREQUAL "armeabi-v7a")
#        set(ANDROID_EXTRA_LIBS
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so)
#    endif()
#endif()

find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED)

if(ANDROID)
  add_library(miniprogram SHARED
    main.cpp
    mainwindow.cpp
    mainwindow.h
    mainwindow.ui
    subthread.cpp
    subthread.h
  )
else()
  add_executable(miniprogram
    main.cpp
    mainwindow.cpp
    mainwindow.h
    mainwindow.ui
    subthread.cpp
    subthread.h
  )
endif()

target_link_libraries(miniprogram PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "subthread.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
  Q_OBJECT

public:
  MainWindow(QWidget *parent = nullptr);
  ~MainWindow();

private slots:
  void on_pushButton_clicked();

  void on_pushButton_2_clicked();

private:
  Ui::MainWindow *ui;
  SubThread *subThread;
};
#endif // MAINWINDOW_H

subthread.h

#ifndef SUBTHREAD_H
#define SUBTHREAD_H

#include <QThread>

class SubThread : public QThread
{
public:
  SubThread();

protected:
  void run() override;
};

#endif // SUBTHREAD_H

main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
  QApplication a(argc, argv);
  MainWindow w;
  w.show();
  return a.exec();
}

mainwindow.cpp

#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include "subthread.h"

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

  subThread = new SubThread;
}

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


void MainWindow::on_pushButton_clicked()
{
  qInfo("Start subthread from mainwindows");
  subThread->start();
}


void MainWindow::on_pushButton_2_clicked()
{
  qInfo("Stop subthread from mainwindows");
  subThread->quit();
}

subthread.cpp

#include "subthread.h"

#include <iostream>

SubThread::SubThread()
{

}
// protected member
void SubThread::run()
{
  qInfo("Thread running...");
  while(true)
  {
    usleep(300000);  // 300ms
    std::cout << "." << std::flush;
  }
  qInfo("Thread exists...");
}

mainwindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>487</width>
    <height>333</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QPushButton" name="pushButton">
    <property name="geometry">
     <rect>
      <x>200</x>
      <y>140</y>
      <width>89</width>
      <height>25</height>
     </rect>
    </property>
    <property name="text">
     <string>Start</string>
    </property>
   </widget>
   <widget class="QPushButton" name="pushButton_2">
    <property name="geometry">
     <rect>
      <x>350</x>
      <y>140</y>
      <width>89</width>
      <height>25</height>
     </rect>
    </property>
    <property name="text">
     <string>Stop</string>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>487</width>
     <height>22</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

[ Updates ]

After I have read QEventLoop document, I find that is not what I want. So I use communication flag in that while loop, like

 // ...
 while(!quitflag)
 {

 }
 // ...

What have I missed? Thanks in advance.

You are calling method quit() but the documentation is declaring:

This function does nothing if the thread does not have an event loop.

I cite your code (I can not edit your post yet)

void SubThread::run()
{
  qInfo("Thread running...");
  while(true)
  {
    usleep(300000);  // 300ms
    std::cout << "." << std::flush;
  }
  qInfo("Thread exists...");
}

Your code above has no any action to quit thread - id do just an infinite loop.

this is the code - no magic - it will not finish itself

To quit thread you have many option, for example:

  • use requestInterruption() and isInterruptionRequested() method pair of class QThread
  • check some local variable - for example quit_requested - and finish infinite your loop when it is requested (like this while(quit_requested) ) - and set that variable at button handler (via access method of course)
  • use some interthreading communication
  • use event loop according doc

example (first option):

void SubThread::run()
{
  qInfo("Thread running...");
  while(!QThread::currentThread()->isInterruptionRequested())
  {
    usleep(300000);  // 300ms
    std::cout << "." << std::flush;
  }
  qInfo("Thread exists...");
}

// finish thread from button handler
void MainWindow::on_pushButton_2_clicked()
{
  qInfo("Stop subthread from mainwindows");
  subThread->requestInterruption();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM