2017-09-18 14 views
1

私は、GNU/Linux、MacOS、Windows上でさまざまなpcscの実装を使用してカードリーダーに接続するQtアプリケーションを持っています。カードとのすべての通信はワーカースレッドで実行されます。pcscコールを実行しているQThreadのブロックを解除するには?

ユーザは、カードリーダを介してカードとの通信を必要とする動作を開始する。カードリーダにはキーボードがあり、認証手続き中にユーザはPINをリーダーのキーボードに入力する必要があります。

この操作は、SCardControl()(例:the Microsoft documentationを参照)を呼び出すことによって実装されます。ユーザーがリーダーで作業している間は、SCardControl()への呼び出しは終了せず、ワーカースレッドはそのスレッドによってブロックされます。

この時点で、ユーザーは操作が保留中の間にアプリケーションを閉じることを決定することがあります。ワーカースレッドがSCardControl()が戻るのを待ってブロックされている

  1. :ので、この時点でアプリケーションを閉じると、(信号SIGABRTでLinux上)がクラッシュするアプリケーションが発生します。
  2. メインスレッドはブロックされたスレッドを停止できません。quit()terminate()もスレッドを終了させません。
  3. アプリケーションが終了すると、ワーカースレッドのオブジェクトQThreadが破棄され、スレッドはまだ実行状態であるため、エラーを示す信号がスローされます。

私はいくつかの解決策を試しました。

  1. サブクラスQThreadQThread::terminate()て終了を許可するようにsetTerminationEnabled(true);を呼び出すワーカースレッドを作成します。これはMacOSでは機能しません。QThreadが破棄された場合、スレッドはまだ実行状態にあり、信号SIGABRTが発行されます。
  2. シャットダウン時にハンドルシグナルSIGABRTを処理し、無視します。これはいい考えではありませんでしたが、私はそれを捨てる前に試してみたかったのです。信号SIGABRTを無視した後、信号SIGSEGVが受信され、アプリケーションがクラッシュします。私はhereと記載された手法を採用しました。
  3. メインスレッドからカードリーダーにコマンドを送信してスレッドのブロックを解除してください。私はSCardCancel()SCardDisconnect()SCardReleaseContext()を試しましたが、これらのコマンドはブロックされたスレッドに影響を与えませんでした。

一部の関数呼び出しでスレッドがブロックされても、アプリケーションを完全にシャットダウンすることはできないが、私が試したすべての解決策は機能しておらず、アイデアが足りなくなっている。私は何か見落としましたか?誰も便利なヒントを持っていますか?

EDIT

私はQThreadのためのQtのソースコードに見て、Unixライクなプラットフォーム上でQThread::terminate()が内部pthread_cancel()を使用していることが分かりました。しかし、明らかにpthread_cancel()は動作しません/ Darwinに何もしません。 hereおよびhere

だから、実際には、読者からカードを削除するように求めるダイアログをユーザに表示するオプションを選択する必要があります。

答えて

0

通話中にブロックされていると、外部からスレッドをきれいにシャットダウンすることはできません。ただし、操作が完了する前にユーザーがアプリケーションを終了できないようにすることはできます。

void MainWindow::closeEvent(QCloseEvent *closeEvent) { 
    if (workerBlocked) closeEvent->ignore(); 
} 

さらに、操作を最初に完了する必要があることをユーザーに知らせるダイアログを表示できます。可能であれば

また、あなたは、ウィンドウのクローズを聞かせすることができますが、操作は問題がQThreadオブジェクトが関連付けられている間、破壊ではないという事実に帰着qApp->setQuitOnLastWindowClosed(false);

+0

"通話中にブロックされていると、外部からスレッドをきれいにシャットダウンすることはできません。":スレッドがアプリケーション全体をハイジャックする可能性があるのは非常に驚きです。 –

0

を設定することにより完了するまで生きたアプリケーションを保ちますスレッドが実行中です。通常、それだろうデバッグ出力には、このようなprint文:

QThread:破壊されたスレッドがまだ実行されている間

そう返すように SCardControlを取得しようとしている以上にアゴナイズしないでください

そのワーカースレッドユーザーが読者とやり取りしている間は返されないため、安全に終了することができます。代わりに、this answerに従うことで、現在の実装を最小限に変更して安全な方法でQThreadオブジェクトを破棄することができます。関連するスレッドの実行が完了するまでSafeThreadのデストラクタがwait()に確認しますことを

#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" 

注意:ここで

は、私が何を意味するかを示す例です。その後、メインスレッドはQThreadのデストラクタを呼び出すことができます。

+0

"SafeThreadのデストラクタは、関連スレッドが実行を終了するまでwait()を確認し、その後はメインスレッドがQThreadのデストラクタを呼び出すことができることに注意してください。":正しく理解すれば、アプリケーションはユーザーがスレッドをブロック解除するなど、リーダーからカードを取り除くなど、何らかのアクションを実行するまで終了します。代わりに、私は外部ライブラリ関数への別の呼び出しを持っていると仮定し、それは終了しません。スレッドを強制終了し、アプリケーションを正常にシャットダウンするにはどうすればよいですか? –

+0

私は 'QThread :: terminate()'を使い方を決して呼ぶことはありません。私は読者が接続されている場合、closeイベントをチェックして、arhzuの答えに示されているように、ユーザーに切断を促すプロンプトを表示します。 – Mike

+0

とにかく、デストラクタの 'quit()'コールを 'terminate()'コールで置き換えて、あなたのためにどのように行くのかを見ることができます。しかし、もしあなたがこのルートに行くなら、読者を削除することによってエラーを強制する以外の方法で 'SCardControl'呼び出しを返すような、より清潔な方法がないことを確かめてください。 – Mike

関連する問題