C++ 调用exit()quit()或terminate()后,Qt线程执行不会停止
首先,我已经阅读了s和使用,但我不完全确定我的实现是否正确 TL;DR请参见下面的问题详细信息 最有用的信息来源是,演示文稿(对于w/&w/o事件循环很有用),所以发布并与此问题相关 我的场景是: 我有一个可能运行很长时间的函数,可以调用多个I/O磁盘。因此,我需要一个线程来阻止UI。为此,我自己实现了一个线程 TL;DR QThreads 我的理解是,线程是一个单独的事件循环对象,需要一个自定义实现,或者将一个对象移动到新创建的线程对象,移动的对象在该线程对象中生存(并运行)。我描述的是事件循环实现 问题 我可能遗漏了一些东西,因为我的实现,我上面描述的,不能正常工作。我怎么知道这一点,Qt文档和上面的文章提到了或依赖于,如果没有运行(通过调用via),那么quit()或exit()函数将永远不会运行,这是我的问题 我的实现philisophy与Java的线程和Lambda语法类似,例如C++ 调用exit()quit()或terminate()后,Qt线程执行不会停止,c++,multithreading,qt,qthread,qeventloop,C++,Multithreading,Qt,Qthread,Qeventloop,首先,我已经阅读了s和使用,但我不完全确定我的实现是否正确 TL;DR请参见下面的问题详细信息 最有用的信息来源是,演示文稿(对于w/&w/o事件循环很有用),所以发布并与此问题相关 我的场景是: 我有一个可能运行很长时间的函数,可以调用多个I/O磁盘。因此,我需要一个线程来阻止UI。为此,我自己实现了一个线程 TL;DR QThreads 我的理解是,线程是一个单独的事件循环对象,需要一个自定义实现,或者将一个对象移动到新创建的线程对象,移动的对象在该线程对象中生存(并运行)。我描述的是事件循
new Thread(() -> { // some code to run in a different thread}).start();
我使用了以下实现
可以使用lambda的线程对象容器
简单的示例用法是(线程和子对象清理在QWorkerThread
内部完成):
问题细节/示例
// somewhere in main UI thread
workerThread->stop(); // or workerThread->kill()
调用QThread::quit()
或QThread::quit()
,然后调用QThread::terminate()
,然后再调用QThread::wait()
将不会终止线程。lambda中定义的长时间运行的进程(在setRunnable()
中)将一直运行到完成为止
我知道这篇文章的篇幅比传统的要长,但我更希望每个人都能全面了解我正在努力实现的目标,因为我不确定我的问题到底在哪里
任何帮助都将不胜感激
代码实现 我将张贴所有代码的完整想法的实现,以防我错过了一些重要的东西 QWaitThread.h是
QThread
#ifndef QWAITTHREAD_H
#define QWAITTHREAD_H
#include <QObject>
#include <QThread>
#include <QWaitCondition>
#include <QMutex>
class QWaitThread : public QThread
{
Q_OBJECT
public:
explicit QWaitThread(QObject *parent = nullptr);
~QWaitThread();
virtual void pause();
virtual void resume();
signals:
void paused();
void resumed();
public slots:
void pausing();
void resuming();
private:
QWaitCondition *waitCondition;
QMutex mutex;
};
#endif // QWAITTHREAD_H
#ifndef THREADWORKER_H
#define THREADWORKER_H
#include <QObject>
#include <functional>
#include <QWaitCondition>
#include <QMutex>
#include "QInterruptable.h"
class ThreadWorker : public QObject, public QInterruptable
{
Q_OBJECT
private:
QMutex mutex;
QWaitCondition *waitCondition;
std::function<void ()> runnable;
bool shouldPause = false;
public:
explicit ThreadWorker(QObject *parent = nullptr);
ThreadWorker(std::function<void ()> func);
~ThreadWorker();
void setRunnable(const std::function<void ()> &value);
signals:
/**
* Emitted when the QWorkerThread object has started work
* @brief started
*/
void started();
/**
* @brief progress reports on progress in method, user defined.
* @param value reported using int
*/
void progress(int value);
/**
* Emitted when the QWorkerThread object has finished its work, same signal is used from &QThread::finished
* @brief started
*/
void finished();
/**
* Emitted when the QWorkerThread has encountered an error, user defined.
* @brief started
*/
void error();
public slots:
virtual void run();
virtual void cleanup();
// QInterruptable interface
public:
void pause()
{
shouldPause = true;
}
void resume()
{
shouldPause = false;
}
QMutex& getMutex();
QWaitCondition *getWaitCondition() const;
void setWaitCondition(QWaitCondition *value);
bool getShouldPause() const;
// QInterruptable interface
public:
void interrupt()
{
}
};
#endif // THREADWORKER_H
接口定义了一些预期的功能
#ifndef QINTERRUPTABLE_H
#define QINTERRUPTABLE_H
class QInterruptable {
public:
virtual void pause() = 0;
virtual void resume() = 0;
virtual void interrupt() = 0;
virtual ~QInterruptable() = default;
};
#endif // QINTERRUPTABLE_H
ThreadWorker.h是在QWaitThread
#ifndef QWAITTHREAD_H
#define QWAITTHREAD_H
#include <QObject>
#include <QThread>
#include <QWaitCondition>
#include <QMutex>
class QWaitThread : public QThread
{
Q_OBJECT
public:
explicit QWaitThread(QObject *parent = nullptr);
~QWaitThread();
virtual void pause();
virtual void resume();
signals:
void paused();
void resumed();
public slots:
void pausing();
void resuming();
private:
QWaitCondition *waitCondition;
QMutex mutex;
};
#endif // QWAITTHREAD_H
#ifndef THREADWORKER_H
#define THREADWORKER_H
#include <QObject>
#include <functional>
#include <QWaitCondition>
#include <QMutex>
#include "QInterruptable.h"
class ThreadWorker : public QObject, public QInterruptable
{
Q_OBJECT
private:
QMutex mutex;
QWaitCondition *waitCondition;
std::function<void ()> runnable;
bool shouldPause = false;
public:
explicit ThreadWorker(QObject *parent = nullptr);
ThreadWorker(std::function<void ()> func);
~ThreadWorker();
void setRunnable(const std::function<void ()> &value);
signals:
/**
* Emitted when the QWorkerThread object has started work
* @brief started
*/
void started();
/**
* @brief progress reports on progress in method, user defined.
* @param value reported using int
*/
void progress(int value);
/**
* Emitted when the QWorkerThread object has finished its work, same signal is used from &QThread::finished
* @brief started
*/
void finished();
/**
* Emitted when the QWorkerThread has encountered an error, user defined.
* @brief started
*/
void error();
public slots:
virtual void run();
virtual void cleanup();
// QInterruptable interface
public:
void pause()
{
shouldPause = true;
}
void resume()
{
shouldPause = false;
}
QMutex& getMutex();
QWaitCondition *getWaitCondition() const;
void setWaitCondition(QWaitCondition *value);
bool getShouldPause() const;
// QInterruptable interface
public:
void interrupt()
{
}
};
#endif // THREADWORKER_H
在哪里
d->running == true
d->finished == false
d->data->isAdopted ?
我已经测试了您的代码,下面是我的体会。
正如您所提到的,
terminate()
不会完全停止线程。
Qt doc说:
终止线程的执行。根据操作系统的调度策略,线程可以立即终止,也可以不立即终止请务必在终止()之后使用QThread::wait()
。
不幸的是,wait()
即使在terminate()
之后也会冻结。您的代码可能有问题,但我创建了一个最大限度简化的示例来验证这一点,它仍然存在相同的问题
首先,以下是我建议更改的代码部分:
QWorkerThread::~QWorkerThread()
{
...
// cleanup
delete workerObject; // Unsafe, but the only way to call the destructor, if necessary
delete workerThread; // qFatal
}
以下是Qt doc关于析构函数不安全的说法:
从拥有对象的线程以外的线程(或以其他方式访问对象)对QObject
调用delete是不安全的,除非您保证对象当时没有处理事件。改为使用QObject::deleteLater()
,将发布一个DeferredElete
事件,对象线程的事件循环最终将接收该事件。默认情况下,拥有QObject
的线程是创建QObject
的线程,但不是在调用QObject::moveToThread()
之后
注意。将delete workerThread
更改为workerThread->deleteLater()
在没有qFatal
的情况下对我有效
好的,我们实际上有什么问题:
qFatal
wait()
#pragma once
#include <QObject>
class Worker : public QObject
{
Q_OBJECT
public:
~Worker();
public slots:
void process();
};
#pragma once
#include <QtWidgets/QMainWindow>
class QThread;
class Worker;
class MainWin : public QMainWindow
{
Q_OBJECT
public:
MainWin(QWidget *parent = nullptr);
~MainWin();
private:
QThread* thread = nullptr;
Worker* worker = nullptr;
};
#pragma once
#include <QObject>
#include <functional>
#include <QFuture>
class QThreadPool;
class LambdaThread : public QObject
{
Q_OBJECT
public:
// maxThreadCount = -1 to use idealThreadCount by default
LambdaThread(QObject *parent, int maxThreadCount = -1);
signals:
void started();
void finished();
public slots:
// Invoke this directly or by a signal
QFuture<void> setRunnable(std::function<void()> func);
private:
/*
For the case you need persistent thread sometimes.
In the case you never need persistent thread,
just remove m_threadPool from this class at all
*/
QThreadPool* m_threadPool = nullptr;
};
#pragma once
#include <QtWidgets/QMainWindow>
#include <functional>
class LambdaThread;
class MainWin : public QMainWindow
{
Q_OBJECT
public:
MainWin(QWidget *parent = nullptr);
signals:
// For the case you want to use signals
void newRunnable(std::function<void()> func);
private:
LambdaThread* m_lambdaThread = nullptr;
};
结论和建议
除了避免terminate()
之外,我能提供的一切就是在不使用wait()
的情况下使用terminate()
,然后使用workerThread->deleteLater()
如果您想终止的耗时操作是您自己的代码,请考虑将一些终止标志嵌入到代码中。
如果可能的话,最好避免使用原始指针,用智能指针代替它们我还可以提供什么作为在线程中运行lambdas的通用方法 简化示例如何使用LAMDA、信号插槽、线程、已启动已完成信号、
QtConcurrent::run()
和QFuture
。通过这种方式,您既可以在一个持久的附加线程中运行代码,也可以在自动线程池中运行代码。但不支持终止
LambdaThread.h
#pragma once
#include <QObject>
class Worker : public QObject
{
Q_OBJECT
public:
~Worker();
public slots:
void process();
};
#pragma once
#include <QtWidgets/QMainWindow>
class QThread;
class Worker;
class MainWin : public QMainWindow
{
Q_OBJECT
public:
MainWin(QWidget *parent = nullptr);
~MainWin();
private:
QThread* thread = nullptr;
Worker* worker = nullptr;
};
#pragma once
#include <QObject>
#include <functional>
#include <QFuture>
class QThreadPool;
class LambdaThread : public QObject
{
Q_OBJECT
public:
// maxThreadCount = -1 to use idealThreadCount by default
LambdaThread(QObject *parent, int maxThreadCount = -1);
signals:
void started();
void finished();
public slots:
// Invoke this directly or by a signal
QFuture<void> setRunnable(std::function<void()> func);
private:
/*
For the case you need persistent thread sometimes.
In the case you never need persistent thread,
just remove m_threadPool from this class at all
*/
QThreadPool* m_threadPool = nullptr;
};
#pragma once
#include <QtWidgets/QMainWindow>
#include <functional>
class LambdaThread;
class MainWin : public QMainWindow
{
Q_OBJECT
public:
MainWin(QWidget *parent = nullptr);
signals:
// For the case you want to use signals
void newRunnable(std::function<void()> func);
private:
LambdaThread* m_lambdaThread = nullptr;
};
#pragma一次
#包括
#包括
#包括
类线程池;
类LambdaThread:公共QObject
{
Q_对象
公众:
//maxThreadCount=-1,默认情况下使用idealThreadCount
LambdaThread(QObject*parent,int-maxThreadCount=-1);
信号:
void start();
无效完成();
公众时段:
//直接调用或通过信号调用
QFuture可设置
#include "MainWin.h"
#include "Worker.h"
#include <QThread>
#include <QDebug>
#include <QDateTime>
MainWin::MainWin(QWidget *parent)
: QMainWindow(parent)
{
thread = new QThread;
worker = new Worker;
worker->moveToThread(thread);
// Start only one infinite operation
connect(thread, &QThread::started, worker, &Worker::process);
thread->start();
}
MainWin::~MainWin()
{
if (thread->isRunning())
{
thread->exit(-1);
thread->wait(500);
}
if (thread->isRunning())
{
thread->terminate();
}
//cleanup
delete worker;
delete thread; // qFatal("QThread: Destroyed while thread is still running")
}
MainWin::~MainWin()
{
...
//cleanup
delete worker; // Worker destructor will be called, but be note this is unsafe
thread->deleteLater(); // Allows to avoid qFatal but make thread terminated
}
#pragma once
#include <QObject>
#include <functional>
#include <QFuture>
class QThreadPool;
class LambdaThread : public QObject
{
Q_OBJECT
public:
// maxThreadCount = -1 to use idealThreadCount by default
LambdaThread(QObject *parent, int maxThreadCount = -1);
signals:
void started();
void finished();
public slots:
// Invoke this directly or by a signal
QFuture<void> setRunnable(std::function<void()> func);
private:
/*
For the case you need persistent thread sometimes.
In the case you never need persistent thread,
just remove m_threadPool from this class at all
*/
QThreadPool* m_threadPool = nullptr;
};
#include "LambdaThread.h"
#include <QtConcurrent/QtConcurrent>
#include <QThreadPool>
LambdaThread::LambdaThread(QObject *parent, int maxThreadCount /*= -1*/)
: QObject(parent)
{
m_threadPool = new QThreadPool(this);
if(maxThreadCount > 0)
{
m_threadPool->setMaxThreadCount(maxThreadCount);
if (maxThreadCount == 1)
{
// Avoid thread affinity changing
m_threadPool->setExpiryTimeout(-1);
}
}
}
QFuture<void> LambdaThread::setRunnable(std::function<void()> func)
{
return QtConcurrent::run(m_threadPool,
[this, func]()
{
// Be note that you actually need event loop in a receiver thread only
emit started();
func();
emit finished();
});
}
#pragma once
#include <QtWidgets/QMainWindow>
#include <functional>
class LambdaThread;
class MainWin : public QMainWindow
{
Q_OBJECT
public:
MainWin(QWidget *parent = nullptr);
signals:
// For the case you want to use signals
void newRunnable(std::function<void()> func);
private:
LambdaThread* m_lambdaThread = nullptr;
};
#include "MainWin.h"
#include "LambdaThread.h"
#include <QFuture>
#include <QDebug>
MainWin::MainWin(QWidget *parent)
: QMainWindow(parent)
{
m_lambdaThread = new LambdaThread(this);
connect(this, &MainWin::newRunnable,
m_lambdaThread, &LambdaThread::setRunnable);
/*
Do not forget the third (`this`) context variable
while using modern signal-slot connection syntax with lambdas
*/
connect(m_lambdaThread, &LambdaThread::started,
this, []()
{
qDebug() << "Runnable stated";
});
connect(m_lambdaThread, &LambdaThread::finished,
this, []()
{
qDebug() << "Runnable finished";
});
// Set your lambda directly
QFuture<void> future = m_lambdaThread->setRunnable([]()
{
qDebug() << "hello from threaded runnable";
});
// You can also use future (not necessary of course)
//future.waitForFinished();
// Or you can emit your lambda via the signal:
emit newRunnable([]()
{
qDebug() << "hello from threaded runnable which comes from signal";
});
}