【问题标题】:console out in multi-threaded applications在多线程应用程序中控制台输出
【发布时间】:2011-07-26 08:41:26
【问题描述】:

我通常在开发应用程序时打印到控制台以获得有用的调试/跟踪信息。我现在正在工作的应用程序是多线程的,有时我会看到我的printf 相互重叠。

我尝试使用 mutex 同步屏幕,但最终导致应用程序变慢并被阻止。如何解决这个问题?

我知道 MT 日志库,但在使用它们时,由于我记录的太多,我的应用程序变慢了(有点)。

我正在考虑以下想法..而不是在我的应用程序中记录 within 为什么不记录 outside 呢?我想通过套接字将日志信息发送到实际打印在屏幕上的第二个应用程序进程。

你知道任何图书馆已经这样做了吗? 我使用 Linux/gcc。

谢谢

afg

【问题讨论】:

    标签: c++ c multithreading logging


    【解决方案1】:

    您有 3 个选项。按复杂度递增的顺序:

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

    在大多数情况下,我会选择#2。 #1 可以作为起点,但在除最微不足道的应用程序之外的所有应用程序中,您都可能会遇到序列化应用程序的问题。 #2 仍然很简单,简单是件好事,但它的可扩展性也很强。您最终仍会在主应用程序中进行处理,但对于绝大多数应用程序而言,通过将其转为自己的专用应用程序,您将一无所获。

    第 3 位是您将在性能关键型服务器类型应用程序中执行的操作,但使用此方法获得的最小性能提升是 1:很难实现,2:很容易搞砸,3:这不是人们普遍采用这种方法的唯一甚至最令人信服的原因。相反,当人们需要将日志服务与使用它的应用程序分开时,他们通常会采用这种方法。

    【讨论】:

    • 3 也用于当有人不想再次重新发明 syslog/syslog-ng/msyslog/rsyslog 轮时。
    【解决方案2】:

    您使用的是哪个操作系统?

    不确定具体的库,但解决此类问题的经典方法之一是使用日志队列,该队列由写入线程工作,其工作纯粹是写入日志文件。

    您需要注意,无论是使用线程方法还是多进程方法,写入队列可能会备份,这意味着需要通过丢弃条目或减慢应用程序的速度来管理它(这显然是如果是线程方法更容易)。

    通过某种方式对日志输出进行分类也很常见,这样您就可以将代码的一部分记录在高级别,而将代码的另一部分记录在低得多的级别。这使得管理写入文件的输出量变得更加容易,并为您提供了释放带有日志记录的代码的选项,但关闭它以便在安装时可用于故障诊断。

    【讨论】:

    • 嗨。我正在使用 Linux 和 gcc。我明白你的意思。我认为使用无锁队列可以做到这一点,但主要部分(如下面的回复所述)将在协调多个队列的情况下排队的条目方面做一些工作......在这里我看到了很多想法出来。
    【解决方案3】:

    据我所知,关键部分的重量较轻。

    如果你使用 gcc,你可以使用原子访问。 Link.

    【讨论】:

    • 提问者已经在使用互斥锁,如果他在 Windows 上,它本身已经使用了关键部分,至少我看到它们是这样实现的;所以并没有真正回答这个问题。
    • @leetNightshade:我还没有看到 MS Windows 的源代码,但 MSDN 在链接文档中说:临界区对象提供的同步类似于互斥对象提供的同步,除了临界区只能由单个进程的线程使用。事件、互斥体和信号量对象也可以在单进程应用程序中使用,但临界区对象为互斥同步(特定于处理器的测试和设置指令)提供了更快、更有效的机制。
    • 哦,好吧。好吧,当我过去进行研究时,我所有的消息来源都说要像这样创建自己的互斥锁:link 恰好使用了那个。我从未见过任何人做不同的事情。
    • @leetNightshade:如果日志花费的时间比文件打印时间长,则首选单独的日志进程。如果宿主进程崩溃,日志进程有时间从内存中完成日志记录。
    • @leet:CRITICAL_SECTION 是与 Mutex 完全不同的动物。它们具有相似的语义,但并不相同。 CS 是用户模式设备,不能跨进程共享。互斥锁是一个内核设备,因此需要一个上下文切换来同步它们。关键部分缺少上下文切换是关键部分更快的原因。
    【解决方案4】:

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

    好的,您的解决方案是否使用互斥锁进行打印?也许您应该对另一个线程正在处理以打印的消息队列有一个互斥锁;这有可能挂断,但我认为会更快。因此,请使用一个活动的日志记录线程,该线程会旋转等待打印传入的消息。网络解决方案也可以工作,但这需要更多的工作;先试试这个。

    【讨论】:

    • 如果你以某种作业的形式将动态分配的字符串或数据发送到工作线程,你可以避免所有的锁。
    • @John 是的,但是工作线程内部的消息队列本身需要一个锁/互斥锁,不是吗?
    • 只有一个工作线程时不会。即使n 线程正在发送输出,也可以在任何地方都没有锁。
    • @John 假设工作线程正在使用向量,并且您有两个线程试图通过调用添加元素的函数来添加元素。我以为你会遇到死锁,因为两个线程同时尝试扩展和插入元素。
    • 当然,如果您正在使用的硬件不支持多进程消息传递队列,那么您将不得不自己编写。
    【解决方案5】:

    您可以做的是为每个线程设置一个队列,并让日志记录线程定期检查其中的每一个并将消息发布到某处。

    这很容易设置,并且争用的数量可能非常低(只需交换一两次指针,无需锁定任何东西即可完成)。

    【讨论】:

    • 嗨麦克!对我来说这听起来不错,因为没有锁定我预计对应用程序性能的影响很小。我可能在想,经常从一个队列到另一个队列可能不尊重顺序,但是如果我时间戳或将序列号实现为 aotmic vairble(no lock),它应该可以工作。非常感谢您的建议!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-10
    • 1970-01-01
    • 2017-02-25
    相关资源
    最近更新 更多