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

如果您担心线程问题,您可以改为创建一个宏,以确保在输出之前获取互斥锁并在输出之后释放。

就像是:

#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 回答
1

首先,wxWidgets 2.9wxLog本身是 MT 安全的,您可以为每个线程拥有独立的日志目标,因此也许您可以直接使用它而不是自己编写。

其次,使用TryLock()是可疑的,如果您希望能够重新锁定已经属于当前线程的互斥锁,您应该wxMUTEX_RECURSIVE在创建互斥锁时使用并简单地使用Lock()。但是我个人认为使用递归互斥锁是一个主意,因为它会使您的 MT 代码变得不那么清晰并且更难以推理,这几乎总是灾难性的。

最后,依靠某人打电话的整个想法<< endl是错误的。很容易忘记在某个地方执行此操作并让互斥锁处于锁定状态,从而阻止所有其他线程继续执行。您绝对应该创建一个代理对象,将互斥锁锁定在其 ctor 中并在其 dtor 中解锁,并使用它来确保在执行日志记录的语句结束时始终解锁互斥锁。我认为通过使用这种技术,您也应该避免使用递归互斥锁。

于 2012-10-11T12:50:38.917 回答
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中的此链接,该链接解释了每个函数的工作原理。他们的函数返回值,因此您可以使用它来查看程序中出了什么问题。

请参阅下面他们网站上的文档。

wxMutex::Lock

wxMutexError Lock()

锁定互斥对象。

返回值

之一:

wxMUTEX_NO_ERROR 没有错误。

wxMUTEX_DEAD_LOCK 检测到死锁情况。

于 2012-10-11T00:57:49.243 回答