【问题标题】:Where to put the mutex in a logging class?将互斥锁放在日志类中的什么位置?
【发布时间】:2013-11-16 12:53:18
【问题描述】:

首先,我已经有一段时间没有使用任何类型的互斥锁或信号量了,所以请放轻松。

我已经实现了一个通用的日志记录类,它现在只接收来自其他类的消息,并在该消息前面加上日期/时间和调试级别,然后将消息打印到标准输出。

我想实现某种队列或缓冲区来保存许多发送到日志记录类的消息,然后将它们写入文件。

我遇到的问题是我无法决定如何/在哪里保护队列。

以下是我到目前为止所提出的一些伪代码:

logMessage(char *msg, int debugLevel){
    formattedMsg = formatMsg(msg, debugLevel) //formats the msg to include date/time & debugLevel
    lockMutext()
    queue.add(formattedMsg)
    unlockMutex()
}

wrtieToFile(){
    if (isMessageAvailable()) { //would check to see if there is a message in the queue
        lockMutext()
        file << queue.getFirst() //would append file with the first available msg from the queue
        unlockMutex()
    }
}

我的问题是:

  • 我真的需要在两个地方都使用互斥锁吗?
  • 互斥锁真的是我想要的吗?
  • 我想我可能需要一个线程来写入文件部分 - 这听起来是个好主意吗?

仅供参考,我正在寻找一种不使用 Boost 或任何第三方库的方法。

编辑目标平台是 Linux。

EDIT 2 将 formatMsg 移到互斥锁之前(谢谢@Paul Rubel)

【问题讨论】:

  • 如果您存储消息的队列可以同时被不同的类随机访问,并且您的队列很常见,那么您将需要互斥锁或信号量。如果是这种情况,则在将消息写入队列之前获取互斥锁,并在完成后释放它。您在写入文件时也需要这个,即在写入之前获取锁,并在您完成写入消息后释放它。
  • queue.add 和 queue.getFirst 是否保证不抛出? (第一个不太可能。)如果不是,您缺少 lock_guard 或等效项将在异常情况下泄漏锁。

标签: c++ linux queue mutex


【解决方案1】:

关于你真的需要互斥锁。想想如果你不锁东西会发生什么。除非您的队列是线程安全的,否则您可能需要同时保护插入和删除。

想象一下,当您删除第一个元素时,执行上下文会发生变化。 add 可以发现队列处于不一致的状态,然后谁知道会发生什么。

关于创建消息,除非 formatMsg 使用共享资源,否则您可能会将其从锁定部分中取出,这可以增加您的并行度。

将写入文件提取到自己的线程中听起来是一个合理的选择,这样日志记录线程就不必自己进行调用。

【讨论】:

  • 那么通过将写入文件放在另一个线程中,这会减轻调用线程的阻塞吗?或者调用线程只有在向队列中添加 msg 时才会阻塞?
  • 日志线程将执行写入,如果您的“Worker”线程有非日志工作要做,则无需等待。调用线程也必须在插入时阻塞。要记住的一件事是,互斥锁不一定是“公平的”,因此如果没有工作,插入消息的顺序可能不是它们生成的顺序。
【解决方案2】:

如果我错了,请纠正我。来自多个线程的多个调用者都试图同时访问同一个资源。

也许您可以只使用一个互斥锁来包装您的全部日志记录功能。

注意比赛条件。

编辑 读者可以看看这个答案的 cmets 进行一些有价值的讨论

【讨论】:

  • 来自多个线程的多个调用者 = 是。但是,如果我将所有日志记录功能包装在一个互斥体中,那不会阻塞调用线程吗?
  • 确实,您需要让这些线程休眠,直到资源可用
  • 有没有办法避免阻塞调用线程?我希望调用线程继续,而不必等待记录器完成它的事情
  • 好吧,我觉得奇怪的是,您想要:“保留发送到日志记录类的消息,然后稍后将它们写入文件”,如果您的进程在日志记录之前终止,会发生什么情况写入文件?
  • @redhotspike,队列的全部意义在于您不必必须等待记录器完成它的工作,即昂贵的磁盘写入。您可能必须在获得互斥体之前对其进行阻止,但这是一个很小的代价。实际上,您的线程正在将它们的垃圾(日志字符串)放在路边(队列)。除此之外,您不在乎垃圾人(您的日志记录线程)需要多长时间才能将其捡起并将其拖到垃圾场。
【解决方案3】:

您可以定义一个全局变量,其中包含队列或缓冲区中存在的元素数量。这意味着您需要 increment or decrement 这个变量,而 adding dataremoving data 来自缓冲区或队列。因此,您将此变量保存在上述日志框架的 mutex 中。

【讨论】:

  • 如果我使用队列,全局计数器变量将如何受益?我不能只检查不为空然后检索第一项吗?
  • 您需要有队列或缓冲区的限制(大小)。如果您有多个进程写入队列,那么在这种情况下,您需要锁定包含队列中项目数的变量。即使您从队列中检索数据,您也需要减少队列中项目数的值。当某些进程写入队列并且您也尝试检索数据时,可能会出现这种情况。这可能导致死锁。使用全局计数器可以限制读/写操作以及存储队列的当前大小。
  • mohit,不需要全局 ctr。 OP 可能正在使用不需要限制的 std::queue,它可以自行增长;并且 ctr 隐含在 queue.size() 中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-26
  • 2011-03-17
  • 2012-03-12
  • 2011-04-13
  • 1970-01-01
  • 2018-05-23
相关资源
最近更新 更多