文本编辑器可以将每个按键从一个线程传递到另一个线程吗?

Posted

技术标签:

【中文标题】文本编辑器可以将每个按键从一个线程传递到另一个线程吗?【英文标题】:Is it OK for a text editor to pass each keypress from one thread to another? 【发布时间】:2017-09-24 16:24:46 【问题描述】:

我正在实现一个 Win32 控制台文本编辑器,它有一个内部消息队列,用于传递有关要重绘的区域、与插件之间的消息等的信息。我更喜欢默认情况下它是单线程的(如果没有的话发生需要额外的线程)。我正在考虑2个消息队列实现策略:

    使用通用队列和Win32事件,所以我可以使用WaitForMultipleObjectsEx同时等待内部消息和用户输入,同时传递控制台输入句柄和事件句柄。在这种情况下,文本编辑器可以完全存在于单个线程中。 使用 I/O 完成端口。在这种情况下,文本编辑器至少需要两个线程,其中一个调用GetQueuedCompletionStatus 来获取消息,另一个读取用户输入并通过PostQueuedCompletionStatus 将其发送到队列。控制台输入句柄不能重叠和WaitFor* 函数不接受完成端口作为可等待句柄的原因,因此不可能同时等待它们。就像在第一个设置中一样,当没有输入或事件时,两个线程都不会浪费 CPU 时间,但每次按键都必须通过 IOCP 从一个线程传递到另一个线程。

哪种设计总体上更好?

对于文本编辑器来说,通过 IOCP 传递每个按键的性能和延迟是否显着下降?

【问题讨论】:

我不了解 Windows,但是您每秒只能获得很少的按键。计算机速度很快。 即使线程只是在内核模式下等​​待,如果你有无意义的线程,你仍然在浪费内存。 您是否考虑过使用像Qt 这样的跨平台工具包?然后,您将执行该工具包提供的操作 @BasileStarynkevitch 在设计上它必须有一个 Win32 控制台 UI,而不是禁止它有一些其他的,就像 Vim/gVim、Emacs、Neovim 和其他人一样 Replaying input is not the same as reprocessing it. 【参考方案1】:

IOCP 的性能和延迟非常适合您的目的。但是,我不会将每个按键转换为 PostQueuedCompletionStatus。我宁愿 PostQueuedCompletionStatus 一次将多个按键排入队列,无论您从 ReadConsoleInput 获得多少计数。

我认为性能差异很小,根据环境,任何一个都可以更快。而且 WaitForMultipleObjects 更容易实现。

附:你确定你需要消息队列吗?你为什么不在哪个线程中处理这些重绘请求/插件消息,使用例如要保护的关键部分(控制台输出+您的共享状态)?如果您的拥塞很低,例如 100Hz 消息 + 15Hz 按键,并且您会快速处理它们,那么这将使您的延迟比 IOCP 或任何其他队列更低:在低拥塞或短暂锁定的情况下,关键部分甚至不会切换到用于锁定/解锁的内核。这也将简化设计,您的主线程将在阻塞 ReadConsoleInput() 调用时休眠,在此之前无需等待。

【讨论】:

感谢您的回答!我会考虑您建议的同步消息传递,至少对于某些类型的消息来说它看起来要好得多。拥有消息队列 + WaitFor*/GetQueuedCompletionStatus 的原因是可以分别通过 Alertable I/O 或 IOCP 在主线程中执行 I/O,而且所有内容都不需要完全可重入,这是必需的(如果我的理解是正确的)如果一切都是就地调用的。 @jhkouy78reu9wx 当您有多个线程处理完成请求或您有超过 MAXIMUM_WAIT_OBJECTS=64 个并发流时,IOCP 仅比可警报 I/O 好得多。如果你没有那么多流,我的期望是 WaitForMultipleObjects 和 IOCP 一样快。在与异步 IO 相同的队列中处理控制台输入可能会很有趣:用户按下按键最大约 10Hz,异步 IO 可以以 1 KHz 或更高的速率完成,但是在延迟方面您希望优先考虑用户输入。 WaitForMultipleObjects 稍微好一点,因为它按顺序优先处理句柄。

以上是关于文本编辑器可以将每个按键从一个线程传递到另一个线程吗?的主要内容,如果未能解决你的问题,请参考以下文章

为什么永远不应该将NSManagedObject实例从一个线程传递到另一个线程?

是否可以将线程执行转移到另一个线程?

将文本从Textfield传递到另一个ViewController

从另一个线程更改按钮图标/切换按钮

在后台线程中将核心数据从一个数据库复制到另一个数据库

将数据从UI线程传递到c#中的另一个线程