Golang的select语句是如何实现的?

11

我有一些在C++中的阻塞队列,我希望等待其中任意一个队列出现可以弹出的元素。

我所能想到的唯一机制是为每个队列生成一个单独的线程,该线程从其输入队列中弹出并提供到主队列中,原始线程可以在该队列上等待。

每次从一组队列中弹出时,生成N个新线程然后将它们全部终止,似乎会消耗大量资源。

Go语言是否实现了一些更优雅的机制,我是否能够在自己的C++代码中实现?


我认为你不能仅使用标准设施在一个线程中等待多个条件变量。也许你可以反转问题,让队列主动推送到“主”队列中,从而通知当前阻塞的线程。 - melak47
2
Go语言的select实现在这里,或许可以给你一些启示。 - Duru Can Celasun
https://github.com/tylertreat/chan - Martin Gallagher
2个回答

8

我并不一定认为Go的select实现是优雅的,但我认为它在自己的方式上很美丽,并且相当优化。

  • 它特殊处理单个非默认情况下的select
  • 它会改变评估case的顺序,以避免确定性饥饿
  • 它在开始时进行了乐观的首次尝试,寻找已经满足的case
  • 它使用许多仅供运行时知道的内部机制,在每个通道的内部发送者/接收者队列上排队
    • 它使用类似于轻量级goroutine引用的sudog(同一个goroutine可能有很多个sudog),使得快速跳转到goroutine堆栈成为可能
    • 它使用调度器的gopark机制阻止自身,这可以有效地在信号未解除时解除阻塞
    • 当被唤醒并解除阻塞时,通过操作select goroutine的程序计数器,立即进入触发的case处理程序函数

在这个实现中,没有一个主要的革命性想法,但你会非常欣赏每一步都是如何被仔细调整,以便快速、高效并且与通道的概念很好地集成。因此,除非你至少先有chan构造,否则在另一种语言中重新实现Go的select语句并不是很容易。

你可以看看其他语言中可用的重新实现,其中这个想法以不同程度的相似性和有效性进行了重新设计。如果我必须在另一种语言中从头开始重新实现select,我可能首先尝试使用单个共享信号量,如果不行,就切换到更粗糙、稍微休眠一下然后以随机顺序进行检查的策略。


-3
Golang的select语句灵感来自于C语言的select函数(请参阅GNU libc 文档),该函数用于在一组文件描述符上等待I/O。如果您的队列使用套接字或管道进行通信,则可以使用它。

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