Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.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
Multithreading 如何取消阻止运行pcsc调用的QThread?_Multithreading_Qt_Pcsc - Fatal编程技术网

Multithreading 如何取消阻止运行pcsc调用的QThread?

Multithreading 如何取消阻止运行pcsc调用的QThread?,multithreading,qt,pcsc,Multithreading,Qt,Pcsc,我有一个Qt应用程序,它使用GNU/Linux、MacOS和Windows下的各种pcsc实现连接到读卡器。与卡的所有通信都在工作线程中运行 在一种情况下,用户开始需要通过读卡器与卡通信的操作。读卡器有一个键盘,在身份验证过程中,用户必须在读卡器的键盘上输入PIN 此操作通过调用SCardControl()(参见示例)来实现。只要用户正在使用读卡器,对SCardControl()的调用就不会终止,工作线程就会被它阻塞 此时,用户可能决定在操作仍挂起时关闭应用程序。此时关闭应用程序会导致应用程序崩

我有一个Qt应用程序,它使用GNU/Linux、MacOS和Windows下的各种
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(“工作…”;});