2

メインのスレッドと結合可能な処理スレッドの 2 つのスレッドを持つプログラムで使用するために、独自のロギング クラスを実装することになっています。どちらかのスレッドがロガーを使用しているときに、もう一方のスレッドでロガーを使用したくないので、wxMutex を使用することにしました。ロガーは、operator<< を使用して C++ ostream のように動作する必要がありますが、ストリーム操作関数 (std::endl など) がロガーの出力の終わりを示す点が異なります。ミューテックスがロックされているときに同じスレッドが出力を継続できるように、再帰的なミューテックスが必要だと思います。

問題 (私が思うに) は、ミューテックスが完全にロック解除されていないため、他のスレッドがロガーに出力しようとすると、デッドロックが発生することです。

私のコードに欠けているものはありますか?

class Logger{
  private:
    std::ostream* out;
    wxMutex* mut;
  ....
  public:
    template<class T>
    Logger& operator<<(T str){ // accept any type of input and lock outputting until finished
      if(out){
        if(mut->TryLock() == wxMUTEX_BUSY){ // if we didn't get lock
          mut->Lock(); // we block
        }
        if(out){
          (*out) << str;
          out->flush();
        }
        //mut->Unlock(); // leave locked until done outputting (use std::endl or similar)
      }
      return *this;
    }

    // accept stream manipulators and unlock stream output
    Logger& operator<<(std::ostream& (*pf) (std::ostream&)){
      if(out){
        if(mut->TryLock() == wxMUTEX_BUSY){
          mut->Lock();
        }
        (*out) << pf;
        out->flush();
        while(mut->Unlock()!= wxMUTEX_UNLOCKED);
      }
      return *this;
    }
};
4

3 に答える 3

1

まず第一に、wxWidgets 2.9wxLog自体は MT セーフであり、スレッドごとに独立したログ ターゲットを持つことができるため、独自に記述する代わりにそれを使用することができます。

第二に、使用TryLock()は疑わしいです。現在のスレッドに既に属しているミューテックスを再ロックできるようにしたい場合は、ミューテックスを作成するときに を使用し、それにもかかわらずwxMUTEX_RECURSIVE単純に使用する必要があります。Lock()ただし、個人的には、再帰的ミューテックスを使用することは悪い考えだと思います。なぜなら、MT コードが不明確になり、推論が難しくなり、これはほとんど常に壊滅的な結果になるからです。

最後に、電話を誰かに頼るという考え<< endlはまったく間違っています。どこかで実行するのを忘れて、ミューテックスをロックしたままにしておくと、他のすべてのスレッドが続行できなくなります。ctor でミューテックスをロックし、dtor でロックを解除するプロキシ オブジェクトを作成し、それを使用して、ロギングを実行するステートメントの最後でミューテックスが常にロック解除されるようにする必要があります。この手法を使用することで、再帰的なミューテックスも不要になると思います。

于 2012-10-11T12:50:38.917 に答える
1

スレッドの問題が心配な場合は、代わりに、出力前にミューテックスが取得され、出力後に解放されることを確認するマクロを作成できます。

何かのようなもの:

#define LOG(logger, output) \
    do { logger.lock(); logger << output; logger.unlock(); } while (0)

Logger my_logger;
int some_integer = 5;
LOG(my_logger, "Hello world!" << some_integer << std::endl);
于 2012-10-11T06:24:59.313 に答える
0

私は間違っているかもしれませんが、すでに使用されているミューテックスをロックしようとしているようです。

if(mut->TryLock() == wxMUTEX_BUSY) // if TryLock() returns wxMUTEX_BUSY another thread is using it.
{
    mut->Lock(); // A dead lock situation could be detected here because Lock() also returns an error code.
}

TryLock() は通常、ブロックせずにミューテックスを取得しようとします。そのため、ミューテックスがその呼び出しで既にビジーである場合は、別のスレッドがそれを使用していることを意味します。

各機能がどのように機能するかを説明しているwxMutex のこのリンクを見ることができます。それらの関数は値を返すので、それを使用してプログラムで何が問題になっているのかを確認できます。

以下の Web サイトのドキュメントを参照してください。

wxMutex::ロック

wxMutexError Lock()

ミューテックス オブジェクトをロックします。

戻り値

の一つ:

wxMUTEX_NO_ERROR エラーはありませんでした。

wxMUTEX_DEAD_LOCK デッドロック状態が検出されました。

于 2012-10-11T00:57:49.243 に答える