Multithreading 如何取消阻止运行pcsc调用的QThread?
我有一个Qt应用程序,它使用GNU/Linux、MacOS和Windows下的各种Multithreading 如何取消阻止运行pcsc调用的QThread?,multithreading,qt,pcsc,Multithreading,Qt,Pcsc,我有一个Qt应用程序,它使用GNU/Linux、MacOS和Windows下的各种pcsc实现连接到读卡器。与卡的所有通信都在工作线程中运行 在一种情况下,用户开始需要通过读卡器与卡通信的操作。读卡器有一个键盘,在身份验证过程中,用户必须在读卡器的键盘上输入PIN 此操作通过调用SCardControl()(参见示例)来实现。只要用户正在使用读卡器,对SCardControl()的调用就不会终止,工作线程就会被它阻塞 此时,用户可能决定在操作仍挂起时关闭应用程序。此时关闭应用程序会导致应用程序崩
pcsc
实现连接到读卡器。与卡的所有通信都在工作线程中运行
在一种情况下,用户开始需要通过读卡器与卡通信的操作。读卡器有一个键盘,在身份验证过程中,用户必须在读卡器的键盘上输入PIN
此操作通过调用SCardControl()
(参见示例)来实现。只要用户正在使用读卡器,对SCardControl()
的调用就不会终止,工作线程就会被它阻塞
此时,用户可能决定在操作仍挂起时关闭应用程序。此时关闭应用程序会导致应用程序崩溃(在信号为SIGABRT的Linux上),因为:
SCardControl()
返回quit()
还是terminate()
都不会导致线程完成QThread
对象将被销毁,并且由于线程仍处于运行状态,因此它会抛出一个指示错误的信号QThread
并创建调用setTerminationEnabled(true)的工作线程
允许通过QThread::terminate()
终止。这在MacOS
上不起作用:当QThread
被销毁时,线程仍处于运行状态,并发出信号SIGABRT
SIGABRT
,并忽略它。这似乎不是一个好主意,但我想在放弃它之前尝试一下。忽略信号SIGABRT后,接收到信号SIGSEGV,应用程序崩溃。我采用了所描述的方法SCardCancel()
、SCardDisconnect()
和SCardReleaseContext()
但这些命令对被阻止的线程都没有任何影响QThread
的Qt源代码,发现在类Unix平台上QThread::terminate()
在内部使用pthread\u cancel()。但是显然pthread\u cancel()
在Darwin
上不起作用/什么也不做,请参见和
因此,也许我真的必须选择向用户显示一个对话框,请求从读卡器中移除卡。如果线程在调用中被阻塞,则无法从外部完全关闭线程。但是,您可以防止用户在操作完成之前退出应用程序
void MainWindow::closeEvent(QCloseEvent *closeEvent) {
if (workerBlocked) closeEvent->ignore();
}
此外,您可以显示一个对话框,告诉用户必须首先完成操作
此外,如果可能,您可以通过设置qApp->setQuitOnLastWindowClosed(false),让窗口关闭,但保持应用程序活动,直到操作完成代码>如果线程在调用中被阻止,则无法从外部完全关闭线程。但是,您可以防止用户在操作完成之前退出应用程序
void MainWindow::closeEvent(QCloseEvent *closeEvent) {
if (workerBlocked) closeEvent->ignore();
}
此外,您可以显示一个对话框,告诉用户必须首先完成操作
此外,如果可能,您可以通过设置qApp->setQuitOnLastWindowClosed(false),让窗口关闭,但保持应用程序活动,直到操作完成
问题归结为这样一个事实:当相关线程运行时,QThread
对象不可破坏。通常,它会将如下语句打印到调试输出:
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"
#包括
//可以随时销毁的线程
//看http://stackoverflow.com/a/25230470
类SafeThread:publicqthread{
使用QThread::run;
公众:
显式安全线程(QObject*parent=nullptr):QThread(parent){}
~SafeThread(){quit();wait();}
};
//工作者QObject类
班级工作人员:公共QObject{
Q_对象
公众:
显式工作程序(QObject*parent=nullptr):QObject(parent){}
~Worker(){}
Q_槽孔空隙锁紧工作(){
发射开始();
//休眠调用将阻塞工作线程10秒!
//将其视为对SCardControl函数的模拟调用
QThread::sleep(10);
发射完成();
}
Q_信号无效已启动();
Q_信号无效已完成();
};
int main(int argc,char*argv[]){
质量保证申请a(argc、argv);
//设置工作线程和QObject
工人;
安全螺纹;
worker.moveToThread(&thread);
thread.start();
//设置GUI组件
qw;
QVBoxLayout布局(&w);
QPushButton按钮(“开始工作”);
QLabel状态(“空闲”);
layout.addWidget(&按钮);
layout.addWidget(&status);
//连接信号/插槽
QObject::connect(&worker),&worker::started,&status,
[&status]{status.setText(“工作…”;});