【问题标题】:Qt - Close QThreads that are blocked by system-level callsQt - 关闭被系统级调用阻塞的 QThreads
【发布时间】:2012-11-17 05:38:17
【问题描述】:

我有一个线程阻塞,直到从系统资源(如 USB 设备)接收到数据。我之所以选择这个模型,是因为数据量可能会有所不同,并且随时可能收到数据。退出应用程序后,我收到消息“QThread: Destroyed while thread is still running”。我应该如何关闭这些线程?

我查看了其他问题/解决方案,例如:

第一个解决方案涉及使用标志(包含在我的代码中),但是我的线程永远不会到达标志检查。 第二个解决方案使用 QWaitCondition,但似乎与第一个解决方案相同。

我已包含以下代码的精简版本。系统调用的 WaitForSingleObject() 是我实际使用的 (GetOverlappedResult()) 的替代品。

#ifndef CONTROLLER_H
#define CONTROLLER_H

#include <QObject>
#include <QThread>
#include <QReadWriteLock>
#include <QDebug>

#ifdef Q_OS_WIN
    #include <windows.h>
#endif // Q_OS_WIN

#ifdef Q_OS_LINUX
    #include <unistd.h>
#endif // Q_OS_LINUX

////////////////////////////////////////////////
//
//  Worker Object
//
////////////////////////////////////////////////
class Worker : public QObject {
    Q_OBJECT

public:
    QReadWriteLock lock;
    bool running;

public slots:
    void loop() {
        qDebug() << "entering the loop";
        bool _running;
        forever {

            lock.lockForRead();
            _running = running;
            lock.unlock();

            if (!_running) return;

            qDebug() << "loop iteration";

            #ifdef Q_OS_WIN
                HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL);
                WaitForSingleObject(event, INFINITE);
            #endif // Q_OS_WIN

            #ifdef Q_OS_LINUX
                read(0, 0, 1);
            #endif // Q_OS_LINUX
        }
    }
};

////////////////////////////////////////////////
//
//  Controller
//
////////////////////////////////////////////////
class Controller {
public:
    Controller() {
        myWorker.connect(&myThread, SIGNAL(started()), &myWorker, SLOT(loop()));
        myWorker.moveToThread(&myThread);
        myThread.start();
    }

    ~Controller() {
        // Safely close threads

        myWorker.lock.lockForWrite();
        myWorker.running = false;
        myWorker.lock.unlock();

        myThread.quit();

        //myThread.wait();
        //myThread.exit();
        //myThread.terminate();
    }

private:
    QThread myThread;
    Worker myWorker;
};

#endif // CONTROLLER_H

【问题讨论】:

  • 为什么不用Qt自带的IO函数,比如QFile等呢?它们与信号和插槽一起使用,因此您可以只观看bytesReadyRead 信号。
  • @satuon 我正在制作自己的 QIODevice,它可以读写 HID 设备。虽然这在我之前从未发生过,但也许可以使用 QFile 来访问这些设备。我试试看,谢谢。
  • 顺便说一句,您可以通过使其更短更简洁来改进您的问题 - 我读了不到 10%,因为它太长了。此外,您应该只发布代码的相关部分,而不是整个文件。
  • @satuon 使用 QFile 不起作用。虽然 QFile::open() 返回 true,但写入和读取不起作用,可能是因为设备是顺序的。我也尝试了QtSerialPort 库,但 SerialPort::open() 返回 false。尽管我已经从原始问题中删减了一些内容,但这个问题的长度并没有错。代码已经被剥离,只包含 2 个类和 3 个函数。
  • 好的,然后使用WaitForMultipleObjects,并添加第二个对象,例如 HEVENT,当线程需要完成时发送。

标签: multithreading qt blocking system-calls


【解决方案1】:

对于 Linux:

使用 pthread_kill() 向线程发送信号中断了 read(),失败代码为 EINTR。 sigaction()用来注册信号,抛出的信号是SIGUSR1。

// Global scope
void nothing(int signum) {}

...

// Within the start of the thread
pthread_t myThreadID = pthread_self(); // Get the thread ID
struct sigaction action;
action.sa_flags = 0;
sigemptyset(&action.sa_mask);
action.sa_handler = nothing;
sigaction(SIGUSR1, &action, NULL);

...

// When it's time to close the thread
pthread_kill(myThreadID, SIGUSR1);

对于 Windows:

使用 SetEvent() 向 OVERLAPPED 的 hEvent 发送信号用于解除对 GetOverlappedResult() 的阻塞。

// Store a reference to the event
HANDLE myEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

...

// Within the start of the thread
OVERLAPPED overlapped;
memset(&overlapped, 0, sizeof(overlapped));
overlapped.hEvent = myEvent;

...

// When it's time to close the thread
SetEvent(myEvent);

【讨论】:

  • 您的答案在功能上与 QThread 对象已经提供的 quit()、terminate() 和 exit() 调用相同,只是您选择了不同的信号发送。您的“解决方案”不会导致错误的原因是您添加了显式信号处理程序。从理论上讲,您也可以从原始代码中将通用信号处理程序放入 QThread 中,它会以相同的方式解决问题。 (我没有投反对票)
  • 如果有人可以发布正确的答案,我很乐意将接受的答案切换到那个答案。
猜你喜欢
  • 1970-01-01
  • 2020-11-11
  • 1970-01-01
  • 1970-01-01
  • 2017-04-14
  • 2023-03-09
  • 1970-01-01
  • 2014-07-15
  • 2015-05-24
相关资源
最近更新 更多