我有一个Qt应用程序,它使用GNU、MacOS和Windows下的各种pcsc实现连接到读卡器。与卡的所有通信都在一个工作线程中运行。
在一个场景中,用户开始需要通过读卡器与卡进行通信的操作。读卡器有一个键盘,在验证过程中,用户必须在读取器的键盘上输入他们的PIN。
这个操作是通过调用SCardControl()来实现的(参见Microsoft文档 )。只要用户正在使用读取器,对SCardControl()的调用就不会终止,工作线程就会被它阻塞。
此时,用户可能决定在操作仍然挂起时关闭应用程序。此时关闭应用程序将导致应用程序崩溃(在Linux上使用信号SIGABRT),因为:
SCardControl()返回。quit()和terminate()都不会导致线程完成。QThread对象将被破坏,并且由于线程仍在运行状态,它将抛出一个指示错误的信号。我已经尝试过几种解决方案。
QThread,并创建一个工作线程,该线程调用setTerminationEnabled(true);以允许通过QThread::terminate()终止。这在MacOS上不起作用:当QThread被破坏时,线程仍然处于运行状态,并发出信号SIGABRT。SIGABRT并忽略它。这似乎不是一个好主意,但我想尝试一下,然后放弃它。忽略信号SIGABRT后,接收信号SIGSEGV,应用程序崩溃。我已经采用了描述这里的方法。SCardCancel()、SCardDisconnect()和SCardReleaseContext(),但是这些命令对阻塞的线程没有任何影响。当某个线程在某个函数调用上被阻塞时,我发现不可能完全关闭一个应用程序,但我尝试过的所有解决方案都没有起作用,我的想法也用完了。我是不是忽略了什么?有人有什么有用的提示吗?
编辑
我查看了QThread的Qt源代码,发现在类似Unix的平台上,QThread::terminate()在内部使用pthread_cancel()。但显然pthread_cancel()在Darwin上不起作用/什么也不做,例如这里和这里。
所以,也许我将不得不选择显示一个对话框给用户,要求从读取器中删除卡片。
发布于 2017-09-18 19:54:00
如果线程在调用中被阻塞,那么从外部完全关闭线程是不可能的。但是,您可以防止用户在操作完成之前退出应用程序。
void MainWindow::closeEvent(QCloseEvent *closeEvent) {
if (workerBlocked) closeEvent->ignore();
}此外,您还可以显示一个对话框,告诉用户必须先完成操作。
此外,如果可能的话,您可以让窗口关闭,但通过设置qApp->setQuitOnLastWindowClosed(false);使应用程序保持活动直到操作完成。
发布于 2017-09-19 06:28:26
问题归结为QThread对象在关联线程运行时是不可销毁的。通常,它会在调试输出中使用如下的print语句:
QThread:线程仍在运行时被销毁
不要为让SCardControl返回而烦恼,这样就可以安全地退出工作线程(因为只要用户与阅读器交互,它就不会返回)。相反,您可以遵循这个答案以安全的方式解构QThread对象,同时对当前实现进行最小数量的更改。
这里有一个例子,说明了我的意思:
#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的析构函数。
https://stackoverflow.com/questions/46282396
复制相似问题