首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何打开运行pcsc调用的QThread?

如何打开运行pcsc调用的QThread?
EN

Stack Overflow用户
提问于 2017-09-18 14:55:12
回答 2查看 300关注 0票数 1

我有一个Qt应用程序,它使用GNU、MacOS和Windows下的各种pcsc实现连接到读卡器。与卡的所有通信都在一个工作线程中运行。

在一个场景中,用户开始需要通过读卡器与卡进行通信的操作。读卡器有一个键盘,在验证过程中,用户必须在读取器的键盘上输入他们的PIN。

这个操作是通过调用SCardControl()来实现的(参见Microsoft文档 )。只要用户正在使用读取器,对SCardControl()的调用就不会终止,工作线程就会被它阻塞。

此时,用户可能决定在操作仍然挂起时关闭应用程序。此时关闭应用程序将导致应用程序崩溃(在Linux上使用信号SIGABRT),因为:

  1. 工作线程被阻塞,等待SCardControl()返回。
  2. 主线程不能停止阻塞的线程:quit()terminate()都不会导致线程完成。
  3. 当应用程序退出时,工作线程的QThread对象将被破坏,并且由于线程仍在运行状态,它将抛出一个指示错误的信号。

我已经尝试过几种解决方案。

  1. 子类QThread,并创建一个工作线程,该线程调用setTerminationEnabled(true);以允许通过QThread::terminate()终止。这在MacOS上不起作用:当QThread被破坏时,线程仍然处于运行状态,并发出信号SIGABRT
  2. 在关机时处理信号SIGABRT并忽略它。这似乎不是一个好主意,但我想尝试一下,然后放弃它。忽略信号SIGABRT后,接收信号SIGSEGV,应用程序崩溃。我已经采用了描述这里的方法。
  3. 尝试通过从主线程向读卡器发送命令来解除线程的阻塞。我尝试了SCardCancel()SCardDisconnect()SCardReleaseContext(),但是这些命令对阻塞的线程没有任何影响。

当某个线程在某个函数调用上被阻塞时,我发现不可能完全关闭一个应用程序,但我尝试过的所有解决方案都没有起作用,我的想法也用完了。我是不是忽略了什么?有人有什么有用的提示吗?

编辑

我查看了QThread的Qt源代码,发现在类似Unix的平台上,QThread::terminate()在内部使用pthread_cancel()。但显然pthread_cancel()Darwin上不起作用/什么也不做,例如这里这里

所以,也许我将不得不选择显示一个对话框给用户,要求从读取器中删除卡片。

EN

回答 2

Stack Overflow用户

发布于 2017-09-18 19:54:00

如果线程在调用中被阻塞,那么从外部完全关闭线程是不可能的。但是,您可以防止用户在操作完成之前退出应用程序。

代码语言:javascript
复制
void MainWindow::closeEvent(QCloseEvent *closeEvent) {
     if (workerBlocked) closeEvent->ignore();
}

此外,您还可以显示一个对话框,告诉用户必须先完成操作。

此外,如果可能的话,您可以让窗口关闭,但通过设置qApp->setQuitOnLastWindowClosed(false);使应用程序保持活动直到操作完成。

票数 0
EN

Stack Overflow用户

发布于 2017-09-19 06:28:26

问题归结为QThread对象在关联线程运行时是不可销毁的。通常,它会在调试输出中使用如下的print语句:

QThread:线程仍在运行时被销毁

不要为让SCardControl返回而烦恼,这样就可以安全地退出工作线程(因为只要用户与阅读器交互,它就不会返回)。相反,您可以遵循这个答案以安全的方式解构QThread对象,同时对当前实现进行最小数量的更改。

这里有一个例子,说明了我的意思:

代码语言:javascript
复制
#include <QtWidgets>

//a thread that can be destroyed at any time
//see http://stackoverflow.com/a/25230470
class SafeThread : public QThread{
    using QThread::run;
public:
    explicit SafeThread(QObject* parent= nullptr):QThread(parent){}
    ~SafeThread(){ quit(); wait(); }
};

//worker QObject class
class Worker : public QObject {
    Q_OBJECT
public:
    explicit Worker(QObject* parent = nullptr):QObject(parent){}
    ~Worker(){}

    Q_SLOT void doBlockingWork() {
        emit started();
        //the sleep call blocks the worker thread for 10 seconds!
        //consider it a mock call to the SCardControl function
        QThread::sleep(10);
        emit finished();
    }

    Q_SIGNAL void started();
    Q_SIGNAL void finished();
};


int main(int argc, char* argv[]) {
    QApplication a(argc, argv);

    //setup worker thread and QObject
    Worker worker;
    SafeThread thread;
    worker.moveToThread(&thread);
    thread.start();

    //setup GUI components
    QWidget w;
    QVBoxLayout layout(&w);
    QPushButton button("start working");
    QLabel status("idle");
    layout.addWidget(&button);
    layout.addWidget(&status);

    //connect signals/slots
    QObject::connect(&worker, &Worker::started, &status,
                     [&status]{ status.setText("working. . .");} );
    QObject::connect(&worker, &Worker::finished, &status,
                     [&status]{ status.setText("idle");} );
    QObject::connect(&button, &QPushButton::clicked, &worker, &Worker::doBlockingWork);

    w.show();
    return a.exec();
}

#include "main.moc"

请注意,SafeThread的析构函数确保在关联线程完成执行之前执行wait(),并且只有在此之后,主线程才能继续调用QThread的析构函数。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46282396

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档