繁体   English   中英

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

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

我认为这是一个非常简单的问题。 但是尝试了几次之后,我发现我花了比预期更多的时间。 所以我来这里是为了你的帮助。

我的环境

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

我已将我的程序最小化为一个小程序。 这很简单。 有两个按钮,一个启动按钮和一个停止按钮,用于启动和停止由派生自QThread 的SubThread 实例创建的线程。 我用最小程序在这里上传了我的代码。

要重现该问题,请从 QCreator 启动小程序,然后按下开始按钮。 SubThread 线程按预期打印消息。 但是然后按下停止按钮,该线程将不会退出,这是意想不到的。 我在这里停了下来。

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)

主窗口.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

子线程.h

#ifndef SUBTHREAD_H
#define SUBTHREAD_H

#include <QThread>

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

protected:
  void run() override;
};

#endif // SUBTHREAD_H

主.cpp

#include "mainwindow.h"

#include <QApplication>

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

主窗口.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();
}

子线程.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...");
}

主窗口.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>

[更新]

看完QEventLoop文档后,我发现这不是我想要的。 所以我在那个 while 循环中使用通信标志,比如

 // ...
 while(!quitflag)
 {

 }
 // ...

我错过了什么? 提前致谢。

您正在调用方法quit()但文档声明:

如果线程没有事件循环,这个 function 什么都不做。

我引用你的代码(我还不能编辑你的帖子)

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

您上面的代码没有任何退出线程的操作 - id 只是一个无限循环。

这是代码 - 没有魔法 - 它不会自行完成

要退出线程,您有很多选择,例如:

  • 使用 class QThreadrequestInterruption()isInterruptionRequested()方法对
  • 检查一些局部变量 - 例如quit_requested - 并在请求时完成无限循环(像这样while(quit_requested) ) - 并在按钮处理程序中设置该变量(当然是通过访问方法)
  • 使用一些线程间通信
  • 根据文档使用事件循环

示例(第一个选项):

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