2011-11-16 29 views
7

私は実際に私のC++での非同期およびスレッドセーフなロギングを行う方法を探しています。非同期スレッドセーフログは

私はすでにlog4cpp、log4cxx、ブーストのようなスレッドセーフなログソリューションを模索している:ログインまたはRLOGが、それらのすべてがミューテックスを使用しているようです。そして、私が知る限り、ミューテックスは同期ソリューションです。つまり、他のスレッドがメッセージを書き込もうとすると、すべてのスレッドがロックされます。

ソリューションをご存知ですか?

+0

ログファイルに書き込むときにミューテックスを使用しないと、書き込みアクセスが同時に発生するため、クラッシュや混在したログが発生する可能性があります。 –

答えて

0

Windowsプログラムでは、ユーザー定義のWindowsメッセージを使用します。まず、ヒープ上のログエントリにメモリが割り当てられます。その後、ポインタをLPARAMとして、レコードサイズをWPARAMとしてPostMessageが呼び出されます。レシーバウィンドウはレコードを抽出して表示し、ログファイルに保存します。その後、PostMessageが戻り、割り当てられたメモリは送信者によって割り当て解除されます。このアプローチはスレッドセーフであり、mutexを使用する必要はありません。並行処理は、Windowsのメッセージキューメカニズムによって処理されます。 あまり優雅ではありませんが、機能します。

+0

ウィンドウメッセージキューは実際に並行処理方式について保証していますか?私はそれがロックフリーではないと確信しています。 – thiton

+0

私は、ロックフリーであるかもしれません。さらに重要なのは、通常のユーザ空間のP-Cキュー(たとえば、CSを保護しているキューとそれをブロックしているセマフォなど)と比較して遅いことです。 –

+0

OTOHでは、WMQより悪いです - IOCPキューはさらに遅いです!少なくともWMQは、テンプレート化されたものより優れた解決策です(消費者がポーリングする必要があるプロデューサスレッドごとのロックフリーのキュー)。 –

2

ませライブラリは、私の知る限りでは、これを行いません - それはあまりにも複雑です。あなたは自分自身をロールバックしなければならないでしょう。ここではアイデアがあります。スレッドごとのログファイルを作成し、各エントリの最初の項目がタイムスタンプであることを確認してから、ログをマージしてから、 )を使用して最終ログファイルを取得します。

いくつかのスレッドローカルストレージを使用することができます(たとえば、FILEを処理すると、スレッドローカルストレージにストリームオブジェクトを格納することはできません)。このログを各ログ行で調べ、その特定のファイルに書き込みます。

この複雑さは、すべてミューテックスをロックするのに対して、あなたのアプリケーションのパフォーマンス要件はわかりませんが、それが敏感な場合、なぜあなたは(過度に)ログに記録しますか?ロギングなしで必要な情報を取得する他の方法について考えてみましょう。

も考慮すべきもうひとつは、可能な最短の時間のためにミューテックスを使用する、すなわち、あなたのログエントリが最初に構築し、その後、単にファイルに書き込む前に、ロックを取得することです。

5

ロギングにスレッドを1つだけ使用することで、この問題を避けることをお勧めします。ログインするために必要なデータを渡すためには、ロックフリーのFIFOキューを使用することができます(生産者と消費者限り安全なスレッドは厳密に分離され、1つのスレッドだけそれぞれの役割を持っている - 。したがって、あなたは、各プロデューサーのためのキューが必要になります)

高速ロックフリーキューの

例が含まれています:

queue.h:

#ifndef QUEUE_H 
#define QUEUE_H 

template<typename T> class Queue 
{ 
public: 
    virtual void Enqueue(const T &element) = 0; 
    virtual T Dequeue() = 0; 
    virtual bool Empty() = 0; 
}; 

hybridqueue.h:

#ifndef HYBRIDQUEUE_H 
#define HYBRIDQUEUE_H 

#include "queue.h" 


template <typename T, int size> class HybridQueue : public Queue<T> 
{ 

public: 
    virtual bool Empty(); 
    virtual T Dequeue(); 
    virtual void Enqueue(const T& element); 
    HybridQueue(); 
    virtual ~HybridQueue(); 

private: 
    struct ItemList 
    { 
     int start; 
     T list[size]; 
     int end; 
     ItemList volatile * volatile next; 
    }; 

    ItemList volatile * volatile start; 
    char filler[256]; 
    ItemList volatile * volatile end; 
}; 

/** 
* Implementation 
* 
*/ 

#include <stdio.h> 

template <typename T, int size> bool HybridQueue<T, size>::Empty() 
{ 
    return (this->start == this->end) && (this->start->start == this->start->end); 
} 

template <typename T, int size> T HybridQueue<T, size>::Dequeue() 
{ 
    if(this->Empty()) 
    { 
     return NULL; 
    } 
    if(this->start->start >= size) 
    { 
     ItemList volatile * volatile old; 
     old = this->start; 
     this->start = this->start->next; 
      delete old; 
    } 
    T tmp; 
    tmp = this->start->list[this->start->start]; 
    this->start->start++; 
    return tmp; 
} 

template <typename T, int size> void HybridQueue<T, size>::Enqueue(const T& element) 
{ 
    if(this->end->end >= size) { 
     this->end->next = new ItemList(); 
     this->end->next->start = 0; 
     this->end->next->list[0] = element; 
     this->end->next->end = 1; 
     this->end = this->end->next; 
    } 
    else 
    { 
     this->end->list[this->end->end] = element; 
     this->end->end++; 
    } 
} 

template <typename T, int size> HybridQueue<T, size>::HybridQueue() 
{ 
    this->start = this->end = new ItemList(); 
    this->start->start = this->start->end = 0; 
} 

template <typename T, int size> HybridQueue<T, size>::~HybridQueue() 
{ 

} 

#endif // HYBRIDQUEUE_H 
+0

+1:ロックフリーキューとリーダースレッドが実際に行く方法です –

+2

何ですか?各プロデューサの1つのキュー - それをどのように管理するのですか?とにかくこのキューはどのように機能するのですか - キューが空のときにプロデューサは何を待ちますか? 1つのロガースレッド、1つのPCキュー、およびキュープッシュのみのロック(つまり、キューは1つのオブジェクトインスタンスをプッシュする時間(32/64ビットの参照値)にロックされているだけです。 。 –

+0

That!キューにはキューを割り当てることができます。キューにキューを割り当てることができます。コンシューマはキューの配列全体を繰り返し処理します。プロデューサは待機せず、プロデューサはより多くのデータをエンキューします。いくつかの他の仕事をするか、またはOPはいくつかの信号メカニズムをプッシュで導入することができます、クラスは簡単に変更することができますロックについては、OPはそれを避けたいと思いました – Erbureth

5

私はあなたの質問のRIGHを取得する場合ロガーのクリティカルセクションでI/O操作(恐らくファイルへの書き込み)を行うことが心配です。

ブースト:ログは、カスタムライターオブジェクトを定義することができます。 operator()を定義して非同期入出力を呼び出すか、(I/Oを実行している)ログスレッドにメッセージを渡すことができます。

http://www.torjo.com/log2/doc/html/workflow.html#workflow_2b

6

私はあなたの文が間違っていると思う:ミューテックスを使用すると、同期ソリューションと同等の必要はありません。はい、ミューテックスは同期制御用ですが、さまざまな用途に使用できます。ロギングがまだ非同期的に行われている間は、プロデューサのコンシューマキューなどでmutexを使用できます。

正直私はこれらのロギングライブラリの実装を調べていませんが、ロガーがプロデューサのコンシューマキューに書き込みを行い、別のワーカースレッドが書き込みを行う非同期アペンダ(log4jのようなもの)を作成するのが適切ですファイルが提供されていない場合は、ファイル(または別のアペンダーに委譲することもできます)。


編集: はちょうどそれは私が提案し何をAsyncAppenderを提供し、log4cxxで簡単にスキャンを持っていた:受信ロギングイベントバッファ、および非同期接続アペンダに委譲。

+1

+1ポインタ/参照をプッシュするために短時間待ち行列をロックすることと、完全なディスク書き込み操作をロックすることとの間の大きな性能差を強調する。 –

1

ロックフリーアルゴリズムは必ずしも最速のアルゴリズムではありません。境界を定義します。ロギングにはいくつのスレッドがありますか?多くの場合、1回のログ操作でどれくらいの量が書き込まれますか?

I/Oバインド操作は、スレッドのブロッキング/アウェイクによるスレッドコンテキストの切り替えよりもはるかに低速です。 10の書き込みスレッドでロックフリー/スピンロックアルゴリズムを使用すると、CPUに負荷がかかります。

まもなく、ファイルに書き込むときに他のスレッドをブロックしてください。

+0

ログエントリをキューにプッシュしているときに、他のスレッドを短時間ブロックします。 –