2016-08-02 5 views
8

私はマルチスレッドアプリケーションをQtで作成しています(複数のスレッドに独自のイベントループがあります)。ロギング時に、ログにスレッドID(意味のある名前を含む)をログに記録するようにします。 Qtのデフォルトロガーはこれを行うことができないようです。 (これはおそらく最悪の方法ですが、私はわからないんだけど、これはミューテックスを伴う) ロガー用に別のスレッドを作成していますか?

  • は、専用のロガースレッドや他のあります、すべてのスレッドがそれ自体でをロギングん

    1. : は、だから私は3つのオプションを持っていますスレッドは、直接(おそらく速く、3よりも)それに2と同じ
    2. をイベントをポストが、メッセージが(実際には、これは同様にイベントをポストになります)シグナル/スロットシステムを通じてディスパッチされます。

    どちらが優れていて、一般的にベストプラクティスは何ですか?


    コメント内の質問の後に明確にするいくつかの点:

    • QThreadは、スレッドセーフである標準的な方法postEvent()を、持っています。

    そこで問題になる、ロガースレッドが本質であるキューのいくつかの並べ替え

    • 渡ってイベントのデータをマーシャリングのコストを正当化するために、イベントごとに十分な仕事をする必要がありません問題の私は最高の答えが "Measure!"であることを知っていますが、現在のところ、このアプリは初期の開発段階にあり、測定するものはあまりありません。また、最初から適切なデザインを選択することは常に良いことです。
    • 私の場合、スレッドはおそらく良いアイデアです:それはGUIプレーヤー、再生スレッド、DB /メディアライブラリスレッド、ネットワークスレッドプールのようなメディアプレーヤーです...スレッドの動物園全体。
  • +1

    あるレベルでは、すべての場合に同期が必要です(つまり、mutexes)。だから問題は、ロガースレッドは、イベントソースのスレッドで作業を実行するだけでなく、イベントを書き込むために十分長い間、イベントのデータを何らかのキューにマーシャリングするコストを正当化するために、イベントごとに十分な作業を行う必要があるかどうかです。データを共通出力に出力します。 – nate

    +0

    専用のロギングスレッド(暗黙的かもしれませんが、他のスレッドからのイベントやポストはどこかにキューイングされている必要があります)は同期が必要です。 –

    +0

    @JesperJuhl非パラレルロギングは実行時の動作を変更します(多すぎるような場合(たとえば、何らかの動作をデバッグするためにログレベルが上がった場合など)。専用のロギングスレッドを使用しないと、ログエントリ*を書き込んでいる間に他のスレッドが常にログを記録しないようにするか、混乱させる混乱したログを受け入れる必要があります。 –

    答えて

    8

    Qtの経験がないので、これは一般的なものです。キューイングのコストに関しては、一般にI/Oは通常、他のランタイムコストを淡色にします。したがって、問題はありません。専用のログスレッドの

    プロパティ:

    • グッド:プログラムの実行時の動作に最小限の影響。
    • グッド:保証された単一のログメッセージ(複数のスレッドからの混合出力ではありません)。
    • 悪い:かなりの実装努力。
    • 悪い:ロギングを開始して実際にログを実行する時間が切り離されています(これが全体のポイントです)。期待どおりの場所にログ出力が表示されることがあります。
    • 悪い:終了プログラムは最後の最も重要なログメッセージ(Andreasのポイント)を飲み込んでしまう可能性があるので、同期ログ機能を追加することができます(これは上記のポイントの極端です)。

    各スレッドからのロギングの利点は、上記の逆です。 printf()のような関数が暗黙的にFILEをロックするため、またはログ関数を明示的に同期させるために、2つのスレッドで同時にログすることはできません。これにより、現在のスレッドが完了するまでブロックするすべてのスレッドがブロックされます。デバッグの目的でロギングが行われた場合、バッファリングされていない(ロストクラッシュ時にデータが失われないように)ログを記録し、実行時の影響を悪化させることがあります。

    どれくらい悪いかは、アプリケーションの性質、ロギングメカニズムとデータ量によって異なります。

    +0

    ** I/Oは、通常、他のランタイムコストを淡色にしますので、それは問題ではありません** - それは私が聞きたかったものです!だから今私はこの解決策を考えている:通常、メッセージは専用のスレッドで処理されますが、毎回mutexをロックします(mutexがロックされていれば非常に高速です)。しかし、重要なメッセージは呼び出し元のスレッドで処理され、前述のmutexのためにスレッドセーフです。 –

    2

    を使用してきれいな方法でQtアプリケーションのログ機能を実装しました。

    Qtアプリケーションでは、アプリケーションを表すQApplicationという単一インスタンスがあります。

    QEventから継承して独自のイベントを作成し、投稿し、アプリケーションのQApplicationオブジェクトを使用してそれらを処理することができます。

    したがって、たとえば、あなたのログイベントクラス

    MyLogEvent : public QEvent 
    { 
    public: 
        MyLogEvent(QString threadId, QString logMessage) : QEvent(QEvent::User) 
         { /* Store the ThreadID and log message, with accessor functions */}; 
    } 
    

    があるかもしれないと、あなたがしたい場合、ハンドラは(メインウィンドウオブジェクト可能性があり

    MyLogEvent *event = new MyLogEvent(QString("Thread 1"), QString("Something Happened")); 
    QApplication::postEvent(mainWindow, event); 
    

    を使用して、任意のQtのスレッドからのイベントを投稿することができますウィンドウにログする)、または専用のオブジェクトであれば、たとえばファイルにログする。イベントを処理するオブジェクトで

    、ログメッセージを処理するためのQObject ::イベントを上書き

    bool MainWindow::event(QEvent *e) 
    { 
        if(e->type()==QEvent::User) 
        { 
         // This is a log event 
         MyLogEvent *logEvent = static_cast<MyLogEvent *>(e); 
         ui.textEdit->appendPlainText(logEvent->logMessage()) 
         return true; 
        } 
        return QMainWindow::event(e); 
    } 
    
    1

    自身でログをやって、すべてのスレッドが明示的にミューテックスを使用する必要がありますなぜ私はかなり理解していません。

    ディスクファイルにロギングしている場合は、すべてのスレッドが独自のファイルにロギングできます。あなたは、共通の接頭辞を持つファイルに名前を付けることができます。

    QFile * logFile(QObject * parent = nullptr) { 
        auto baseName = QStringLiteral("MyApplication-"); 
        auto threadName = QThread::currentThread()->objectName(); 
        if (threadName.isEmpty()) 
        return new QTemporaryFile(baseName); 
        else 
        return new QFile(baseName + threadName); 
    } 
    

    オペレーティングシステムは、そのファイルシステムのミューテックス(ES)を経由してアクセスをシリアル化されます。

    適切な並行処理オプションが選択されたsqliteなど、並行アクセスをサポートするデータベースにロギングする場合、データベースドライバはシリアル化アクセスを処理します。

    共通スレッドにログを記録している場合、イベントキューには、postEventのときに自動的にシリアライズするmutexがあります。

    シグナルスロットメカニズムを使用しても、イベントを直接使用することはあまりありません。実際には、より多くのメモリ割り当てを行うことが保証されているので、自分でイベントを投稿することをお勧めします。理想的には、ログメッセージの「最も」に適合するサイズのQVarLengthArray<char>を使用するイベントです。

    // logger.h 
    
    struct MyLogEvent : QEvent { 
        constexpr static QEvent::Type theType() { return (QEvent::Type)(QEvent::User + 1); } 
        QVarLengthArray<char, 128> message; 
        MyLogEvent(const char * msg) : QEvent(theType()) { 
        message.append(msg, strlen(msg)); 
        } 
    }; 
    
    class Logger : public QObject { 
        ... 
    public: 
        static void log(const char * msg) { 
        QCoreApplication::postEvent(instance(), new MyLogEvent(msg)); 
        } 
        static Logger * instance(); // singleton, must be a thread safe method 
    }; 
    
    // logger.cpp 
    ... 
    Q_GLOBAL_STATIC(Logger, loggerInstance); 
    
    Logger * Logger::instance() { 
        // Thread-safe since QGlobalStatic is. 
        return loggerInstance; 
    } 
    

    はあなたがQByteArrayQStringを使用していた、表現new MyLogEventは、少なくとも2つの割り当てを行っているだろう。そこで、このようなイベントは、単一のmallocの呼び出しで行われ割り振ります。

    関連する問題