【问题标题】:Poco log lines from different thread overwrite each other来自不同线程的 Poco 日志行相互覆盖
【发布时间】:2015-07-09 14:07:36
【问题描述】:

我有两个不同的线程都记录到单独的Poco::WindowsColorConsoleChannel 频道。

但有时消息会出现交错,例如如果一个线程记录aaaaaaa,另一个记录bbbbbbbb,那么屏幕可能看起来像abbbbbaaaaaabbb

线程使用完全不同的Logger 对象和不同的通道对象。

如果使用默认频道,也会发生同样的事情。这个问题在颜色控制台通道中尤其明显,因为它使颜色看起来像是错误的线被着色了。

有没有办法让每个日志行“原子”?

注意:我还没有检查登录到SimpleFileChannel 时是否会出现同样的问题,但如果是这样,那么我也需要一个解决方案:)

【问题讨论】:

  • 当然可以将每个对记录器的调用包装在关键部分,但我希望有更好的东西,例如日志框架中内置的东西能够处理这个问题。
  • 您究竟是如何创建这些不同的记录器对象的?
  • @Alex 线程使用Poco::Logger::get("bla"),主应用使用Poco::Application的成员函数logger(),在每种情况下我都使用setChannel附加了WindowsColorConsoleChannel

标签: c++ multithreading logging poco-libraries


【解决方案1】:

ConsoleChannel 日志记录操作受static FastMutex 保护。但是,为了提供正确的 Unicode 文本记录,在 Windows 上使用 ConsoleChannel defaults to WindowsConsoleChannel,它将 UTF-8 转换为 UTF-16。

因此,Application::logger() 可能具有默认的 WindowsConsoleChannel,而线程具有 ConsoleChannel(或其 Color* 版本);在任何情况下,您都必须混合使用不同的通道才能看到您所描述的效果 - 尽管目标相同,但您正在通过不同的通道登录,这些通道受不同的互斥锁保护。我认为 Poco 使用单个静态互斥锁来保护所有控制台通道以避免此类问题是有意义的。

鉴于所有这些,即使没有自定义同步方案,您在上面发布的代码示例也应该可以正常工作。

还请注意,每当您登录到相同的控制台通道类型时,您的线程将在每个日志操作期间相互等待。为避免瓶颈,您可以考虑使用AsyncChannel

(Simple)FileChannel 受non-static FastMutex 保护,因此只要每个线程都登录到自己的文件或它们都通过同一个通道实例登录到同一个文件,它就不会遇到同样的问题。我从来没有尝试过后者,但直觉上这听起来不像是正确的做法。

【讨论】:

  • 是的,我通过两个不同的 WindowsConsoleChannels 登录,因为我希望线程使用不同的配色方案。我猜同样问题的另一种解决方案是通过相同的通道登录,但是为线程添加自定义日志级别,这样我就可以给它们自己的颜色。我会尝试制作一个 MCVE 而不是仅仅描述代码。
  • 通过同一类型的不同通道进行日志记录是线程安全的。通过不同类型的控制台通道从两个线程进行日志记录时会出现此问题。
  • 我肯定使用了两个 WindowsColorConsoleChannel,因为我使用 new Poco::WindowsColorConsoleChannel 创建它们。我会编一个 MCVE,因为如果它应该正常工作,我肯定在其他地方做错了。
【解决方案2】:

我已经做了一个“解决方案”。它有一些缺陷:它可能是在重新发明轮子,对于 Poco 来说可能不是惯用的(我对 Poco 还很陌生),而且我担心如果在线程或应用程序关闭期间抛出异常可能会出现死锁。

但它现在似乎有效。

在头文件中:

    struct RC_Semaphore: Poco::Semaphore, Poco::RefCountedObject
    {
        using Poco::Semaphore::Semaphore;
    };

    struct SemaphoreLock
    {
        SemaphoreLock(Poco::Semaphore &sem): sem(sem) { sem.wait(); }
        ~SemaphoreLock() { sem.set(); }

        Poco::Semaphore &sem;
    };

    struct SynchronizingChannel: Poco::Channel, noncopyable
    {
        SynchronizingChannel(Poco::AutoPtr<RC_Semaphore> sem, Poco::AutoPtr<Poco::Channel> dest)
            : sem(sem), dest(dest) {}

        virtual void log(const Poco::Message& msg)
        {
            SemaphoreLock lock(*sem);
            dest->log(msg);
        }

    private:
        Poco::AutoPtr<RC_Semaphore> sem;
        Poco::AutoPtr<Poco::Channel> dest;
    } ;

用法:

// Synchronization for log channels outputting to console
    auto semConsole = make_AutoPtr<RC_Semaphore>(1);

// Logging channel - main
    auto chanMainRaw = make_AutoPtr<Poco::WindowsColorConsoleChannel>();
    chanMainRaw->setProperty("debugColor", "green");
    auto chanMain = make_AutoPtr<SynchronizingChannel>(semConsole, chanMainRaw);

// Channel that will be used by thread
    auto chanThreadRaw = make_AutoPtr<Poco::WindowsColorConsoleChannel>();
    chanThreadRaw->setProperty("debugColor", "magenta");
    auto chanThread = make_AutoPtr<SynchronizingChannel>(semConsole, chanThreadRaw);

// (other code to set up filters can go here)

    logger().setChannel(chanMain);
    OtherThread::logger().setChannel(chanThread);

注意。 make_AutoPtrstd::make_unique 相同,但使用 Poco::AutoPtr 代替,我使用它来避免重复类型名称。

【讨论】:

    猜你喜欢
    • 2017-10-20
    • 1970-01-01
    • 2022-11-11
    • 1970-01-01
    • 2020-03-08
    • 2017-04-30
    • 1970-01-01
    • 2017-03-23
    • 1970-01-01
    相关资源
    最近更新 更多