【问题标题】:Can I "force" thread to wake up?我可以“强制”线程唤醒吗?
【发布时间】:2014-01-06 16:01:52
【问题描述】:

我想实现一个快速记录器,它保存日志条目,当某个触发器到达时,它会刷新最后 X 条消息。

所以想法是将所有消息保存在一个循环缓冲区中,一旦我们有了触发器,就将它的 ID 推送到另一个线程监控的队列(所有系统中的一个线程)。该线程将返回 X 消息并刷新它们。我知道如何处理在我尝试刷新时正在写入的消息、在我刷新之前已被覆盖的消息、在我尝试更新它们时被刷新的消息等。

我的问题是,例如,如果我有 20 个线程写入消息,并且只有 10 个内核,那么在 2 个“写入器”线程执行之间的时间差内,所有缓冲区将被多次覆盖。

有什么方法可以让“我的”线程“强制”“作者”线程执行(或者给它时间片?我猜没有,但仍然...... 你能建议任何其他方式/设计来克服这个问题吗?

【问题讨论】:

  • 视情况而定,但同时有多个线程写入硬盘驱动器会使硬盘驱动器的速度在 CPU 处理速度饱和之前很久。为简单起见,我只有 1 个写线程:-/。在任何一种情况下,互斥锁或文件锁都有助于确保只有 1 个 (fifo) 线程可以访问缓冲区或写入位置。
  • @MadScienceDreams:是的,我有一位作家(相应地更新了问题)。但问题仍然存在,缓冲区在写入者有机会刷新之前被覆盖
  • 你可以给那个线程一个更高的优先级,所以一旦它发出信号(通过条件变量、互斥体、信号量、读写锁,无论你想要什么),系统调度程序都会执行它线。请注意,您的问题可以通过更好的设计得到更好的解决。如果您的缓冲区在您无法控制的情况下被覆盖,那么您需要重新考虑您的日志记录机制。
  • @Shahbaz:是的,考虑过,不幸的是,这不是一个选项。

标签: c++ multithreading


【解决方案1】:

据我了解,您希望在队列中有新 ID 可用时立即恢复您的线程。锁定原语是可能的——您的编写器线程应该休眠,直到您的触发线程通知。如何实现此行为取决于您使用的多线程框架。

例如,在 C++11 中,您可以查看 std::condition_variable

编辑。 正如 cmets 中提到的,磁盘 IO 很慢,因此您需要在 writer 线程中将消息提取到内存中,然后才将它们写入磁盘。在 IO 期间,缓冲区可以被到达的消息覆盖。

【讨论】:

  • 是否向当前处于休眠状态的线程发出信号,保证它会被唤醒?我用互斥锁试过了,它不起作用,我想到了条件,但我找不到这样的保证。
  • 条件变量a专门用来处理这种情况,所以线程会被唤醒!事实上,即使fake wake-ups 在某些实现中也是可能的。可以用互斥锁来实现条件,但互斥锁主要用于“互斥”。
【解决方案2】:

我之前写过类似的东西,其中对日志方法的调用实际上放在另一个线程 (T-Logger) 监视的队列中。这将其他线程从不必调用底层日志 API 中解放出来,并且在低延迟应用程序中运行良好。

如果您想显式缓冲然后在触发器上写入,那么我仍然建议从一个线程执行所有写入,例如T-Logger,然后使用某种conditional variableT-Logger 发出信号它现在应该将队列中的项目写入底层日志文件。

正如问题 cmets 中提到的,您应该避免让多个线程尝试执行 IO。 IO 非常慢,让所有线程尝试写入文件会导致它们放弃 CPU 周期等待 IO 完成。

【讨论】:

  • @Shahbaz - 是的。我稍微改写了我的答案。
  • 是的,我将只有一个线程进行写入。主要问题是,条件变量是否保证线程一收到信号就会唤醒?我无法找到这样的保证(尽管我可能看的不够深入......)
  • @yosim - 条件变量将导致等待它的任何线程转换到“可运行”状态,并且在某些时候操作系统将在 CPU 上调度线程并运行它。您永远不会得到一个会导致等待线程立即开始运行的解决方案,因为操作系统必须考虑所有线程的调度。
  • 这就是我的想法,不幸的是,它把我们带回了方 1。我使用互斥锁实现了它,但我仍然有问题。看起来需要改变设计。
  • @yosim - 为什么其他线程立即唤醒如此重要?没有操作系统会给你这个保证。
【解决方案3】:

听起来像是使用信号量的经典案例,使用循环缓冲区的长度进行初始化。来自需要记录内容的线程的日志调用必须在继续之前从信号量中获取一个单元,并且记录线程在从队列中提取条目时向信号量发出信号。如果缓冲区用完,任何试图记录的线程都将被阻塞,直到有空间用于其日志条目。

显然,日志条目的循环缓冲区/队列/任何容器都必须是线程安全的。

【讨论】:

  • 你可以在没有信号量的情况下达到同样的效果。只需保留您刷新的最后一个索引,如果您的下一个索引是此索引 - 您应该在缓冲区已满时阻止/执行其他操作。然而,这有两个主要缺陷:第一,记录器不能阻塞。但更重要的是,并非所有消息都需要刷新。很有可能我们很少有消息倒立,甚至没有闪烁一个,然后,刷新所有内容。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-05-27
  • 2020-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-02
相关资源
最近更新 更多