多线程应用程序中的控制台输出

4
通常在开发应用程序时,我习惯于在控制台中打印以获取有用的调试/跟踪信息。由于我现在正在处理的应用程序是多线程的,有时我会看到我的printf重叠在一起。
我尝试使用mutex同步屏幕,但最终导致应用程序变慢和阻塞。如何解决这个问题?
我知道有MT日志记录库,但是使用它们时,由于我记录得太多,我会使我的应用程序变慢(稍微)。
我想到了以下想法...而不是在我的应用程序内部记录日志,为什么不在外部记录日志呢?我想通过套接字将日志信息发送到第二个应用程序进程,实际上在屏幕上打印出来。
您是否知道已经执行此操作的任何库? 我使用Linux/gcc。
谢谢
afg
5个回答

11

你有三种选择。按复杂度递增的顺序排列如下:

  1. 在每个线程中使用简单的互斥锁(mutex)。该互斥锁由所有线程共享。
  2. 将所有输出发送到一个仅用于日志记录的单独线程。
  3. 将所有输出发送到一个单独的日志记录应用程序。

在大多数情况下,我会选择第二个选项(#2)。第一个选项(#1)作为起点是可以的,但在除了最简单的应用程序之外,您可能会遇到序列化应用程序的问题。第二个选项(#2)仍然非常简单,简单易行是好事,而且它也很可扩展。您仍然需要在主应用程序中进行处理,但对于绝大多数应用程序而言,将其拆分成自己的专用应用程序并没有什么意义。

第三个选项(#3)适用于性能关键型服务器类型的应用程序,但采用此方法所获得的极小性能提升具有以下特点: 1: 很难实现,2: 很容易出错,3: 并不是人们通常采用此方法的唯一或最重要的原因。相反,当人们需要将日志记录服务与使用它的应用程序分离时,通常采用此方法。


1
当有人不想再次重新发明syslog/syslog-ng/msyslog/rsyslog轮子时,3也被使用。 - ninjalj

1

@Naszta 那个代码块在技术上是正确的,因为即使互斥体使用临界区,互斥体的构造函数和函数调用也会使其使用起来稍微慢一些。哈哈。 - leetNightshade
@Naszta 在控制台上记录日志“可能”比记录到文件中慢,因为每一行通常都必须通过相关窗口在屏幕上显示。大多数窗口化控制台输出并不是非常快的(因为它们并没有期望那么多的输出被推送到它们)。 - forsvarir
1
@leetNightshade:如果日志记录所需时间比文件打印时间长,则建议使用单独的日志进程。如果主机进程崩溃,日志进程有时间从内存中完成日志记录。 - Naszta
2
@leet:CRITICAL_SECTION和Mutex是完全不同的东西。它们有类似的语义,但并不相同。CS是一种用户模式设备,不能在进程之间共享。Mutex是内核设备,因此需要上下文切换来进行同步。关键部分没有上下文切换是关键部分更快的原因。 - John Dibling
@leet:当然可以。只需给它一个名称。这就是在调用CreateMutex(...)时使用名称参数的目的。 - John Dibling
显示剩余11条评论

1
你使用的是哪个操作系统?
对于这种问题,经典的方法之一是使用一个由写入线程工作的日志队列来记录日志。需要注意的是,无论是采用多线程还是多进程的方法,写入队列都可能会积压,因此需要进行管理,可以通过丢弃条目或减缓应用程序的速度来实现(如果采用多线程方法,则显然更容易)。
通常还会有一些分类日志输出的方式,这样你就可以让代码的某个部分以高级别记录日志,而另一个部分则以低级别记录日志。这样可以更轻松地管理要写入文件的输出量,并为你提供在安装时关闭日志记录但仍可用于故障诊断的选项。

嗨。我正在使用Linux和gcc。我理解你的观点。我认为通过无锁队列可以完成这个任务,但是主要部分(如下面的回复所指出的)将需要一些协调工作,以处理多个队列中入队的条目..在这里我看到了很多想法涌现。 - Abruzzo Forte e Gentile

0

坦白地说,Mutex 是你真正想要做到这一点的唯一方法,所以在你的情况下它总是会很慢,因为你使用了太多的打印语句... 所以要解决你的问题,不要使用太多的 print_f 语句;这就是你开始遇到的问题。

好的,你的解决方案是使用 Mutex 来打印吗?也许你应该有一个 Mutex 到一个消息队列,另一个线程正在处理以打印;这可能会有潜在的挂起,但我认为会更快。因此,使用一个主动记录线程,旋转等待传入的消息进行打印。网络解决方案也可以,但需要更多的工作;先尝试这个。


@John 是的,但工作线程内部的消息队列本身需要一个锁/互斥来保证安全,不是吗? - leetNightshade
2
如果只有一个工作线程,则不需要进行任何锁定,即使有n个线程发送输出,也可以完成此操作。 - John Dibling
@John 我宁愿不依赖于操作系统的消息传递系统,而是编写一个可以在跨平台上运行的自己的系统。除非您认为抽象出一种系统以解决平台消息系统的问题是绝对必要的;然而,您能肯定所有平台都有这样的功能吗?那么游戏机或DS等便携式设备呢?就我所知,我需要创建自己的消息传递系统,不能依赖于Windows 自带的。从这个角度来看,我认为扩展消息队列时仍然需要使用互斥锁/临界区域,原始的问题仍然存在。 - leetNightshade
我无法告诉你一个跨平台的方法,因为我不知道有哪些。但是我可以告诉你,通常是操作系统来管理这个消息队列,而不是程序员。 - John Dibling
1
当然,如果您正在使用的硬件不支持多进程消息队列,您将不得不编写自己的代码。 - John Dibling
显示剩余4条评论

0
你可以做的是每个线程都有一个队列,日志线程定期遍历这些队列,并将消息发布到某个地方。
这很容易设置,争用量可以非常低(只需交换指针或两个指针即可完成,而不需要锁定任何内容)。

嗨,Macke!对我来说听起来不错,因为没有锁定,我预计应用程序性能会受到很小的影响。我可能认为经常从一个队列到另一个队列可能不尊重顺序,但如果我时间戳或者使用作为原子变量实现的序列号,它应该可以工作。非常感谢您的建议! - Abruzzo Forte e Gentile

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接