Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/146.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 调用exit()quit()或terminate()后,Qt线程执行不会停止_C++_Multithreading_Qt_Qthread_Qeventloop - Fatal编程技术网

C++ 调用exit()quit()或terminate()后,Qt线程执行不会停止

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 我的理解是,线程是一个单独的事件循环对象,需要一个自定义实现,或者将一个对象移动到新创建的线程对象,移动的对象在该线程对象中生存(并运行)。我描述的是事件循

首先,我已经阅读了s和使用,但我不完全确定我的实现是否正确

TL;DR请参见下面的问题详细信息

最有用的信息来源是,演示文稿(对于w/&w/o事件循环很有用),所以发布并与此问题相关

我的场景是:

我有一个可能运行很长时间的函数,可以调用多个I/O磁盘。因此,我需要一个线程来阻止UI。为此,我自己实现了一个线程

TL;DR QThreads

我的理解是,线程是一个单独的事件循环对象,需要一个自定义实现,或者将一个对象移动到新创建的线程对象,移动的对象在该线程对象中生存(并运行)。我描述的是事件循环实现

问题

我可能遗漏了一些东西,因为我的实现,我上面描述的,不能正常工作。我怎么知道这一点,Qt文档和上面的文章提到了或依赖于,如果没有运行(通过调用via),那么quit()或exit()函数将永远不会运行,这是我的问题

我的实现philisophy与Java的线程和Lambda语法类似,例如

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()
  • (似乎只有当无限操作移动到事件循环中时,问题才是实际的)

    确保问题不在代码的其他位置 最小重现性示例 Worker.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;
    };
    
    结论和建议 除了避免
    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";
        });
    }