简体   繁体   English

QThread::exit 或 QThread::quit 不会终止由 derived class 创建的线程

[英]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.有两个按钮,一个启动按钮和一个停止按钮,用于启动和停止由派生自QThread 的SubThread 实例创建的线程。 I uploaded my codes here in minimal program .我用最小程序在这里上传了我的代码。

To reproduce that problem, start up miniprogram from QCreator and then push the start button.要重现该问题,请从 QCreator 启动小程序,然后按下开始按钮。 The SubThread thread prints messages as expected. SubThread 线程按预期打印消息。 But then push the stop button, that thread will not exit which is unexpected.但是然后按下停止按钮,该线程将不会退出,这是意想不到的。 I'm stopped here.我在这里停了下来。

CMakeLists.txt 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主窗口.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子线程.h

#ifndef SUBTHREAD_H
#define SUBTHREAD_H

#include <QThread>

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

protected:
  void run() override;
};

#endif // SUBTHREAD_H

main.cpp主.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主窗口.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子线程.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主窗口.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.看完QEventLoop文档后,我发现这不是我想要的。 So I use communication flag in that while loop, like所以我在那个 while 循环中使用通信标志,比如

 // ...
 while(!quitflag)
 {

 }
 // ...

What have I missed?我错过了什么? Thanks in advance.提前致谢。

You are calling method quit() but the documentation is declaring:您正在调用方法quit()但文档声明:

This function does nothing if the thread does not have an event loop.如果线程没有事件循环,这个 function 什么都不做。

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.您上面的代码没有任何退出线程的操作 - id 只是一个无限循环。

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使用 class QThreadrequestInterruption()isInterruptionRequested()方法对
  • 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)检查一些局部变量 - 例如quit_requested - 并在请求时完成无限循环(像这样while(quit_requested) ) - 并在按钮处理程序中设置该变量(当然是通过访问方法)
  • 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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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