【问题标题】:Does Go scheduler also manages non-Go Thread created in runtime?Go 调度程序是否还管理在运行时创建的非 Go 线程?
【发布时间】:2017-02-16 04:20:25
【问题描述】:

据我所知,Go 运行时调度程序通过不断地将 Go 例程分配给 OS 线程来管理一些 OS 线程(可能超过 GOMAXPROCS?)和 Go 例程。

所以这基本上意味着 Go 例程的执行,包括 main goroutine ,由 go scheduler 和 OS 的线程调度共同管理。

现在这是我的问题..

  1. 如果我在 goroutine 开始时调用 runtime.LockOSThread(),goroutine 的执行是否完全由操作系统的线程调度管理?

  2. 非 Go 线程的执行是否也完全由操作系统的线程调度管理?换句话说,如果我通过CreateThread函数(Windows)创建了一个非Go线程,那么管理非Go线程的执行是否超出了Go的运行时调度程序的范围?

  3. 如果我在那个非 Go 线程中使用 go func() 启动另一个 goroutine 会怎样?如何管理非 Go 线程和 goroutine 的执行?

  4. 目前,我正在用 Golang 编写一个程序,它在 go 程序的 main() 函数中运行一个 Windows 消息循环。 大多数情况下它运行良好,但有时消息循环被阻塞并在几秒钟后恢复,然后大量旧消息被抽出。 (我的另一个问题:Windows Message Loop is getting blocked and resumed intermittently (golang)

    我不知道为什么会发生,所以我怀疑主 goroutine 的 OS 线程切换是由 go scheduler 引起的。所以我在main()函数的开头添加了runtime.LockOSThread(),以确保windows消息循环总是在同一个线程中运行。 但是问题还是出现了!

    我仍然不知道它为什么会发生,但我怀疑这是因为 Go 调度程序,因为用 Python 3.4 编写的相同逻辑没有出现这样的问题。

    所以我现在正在尝试通过调用 CreateThread(...) 函数创建一个新的 Windows 线程(非 Go 线程),并在该线程中运行 Windows 消息循环。

    但我很好奇,从 Go 运行时调度程序的角度来看,这种方法与在运行 windows 消息循环的主 goroutine 中调用 runtime.LockOSThread() 是否不同。

    所以我的问题是,'如果我使用 CreateThread(...) 函数创建一个新的非 Go 线程并在该线程中运行 windows 消息循环,那么该线程的执行是否不受 Go 运行时调度程序的影响?'

任何帮助或想法将不胜感激。 谢谢。

【问题讨论】:

  • 我相当肯定,如果没有运行时的支持,您不能安全地使用新的操作系统线程,无论是让运行时产生线程还是使用 cgo。你不能比 LockOSThread 更好地将 goroutine 固定到线程。我认为您需要了解您的应用在被阻止时在做什么。
  • @JimB 感谢您的评论。我很难弄清楚我的应用程序在被阻止时正在做什么。因为这个问题是随机发生的。你能给我任何建议或工具来弄清楚我的程序在被阻塞时在做什么吗?谢谢
  • 检查包runtime 文档,看看您是否可以使用GODEBUG 环境变量设置之一来查看您获得的暂停是否与GC 暂停相对应。你也可以试试这个:使用一些 C 代码中的 CreateThread() 创建一个 Go 不知道的线程,并在那里创建你的窗口和泵消息。这里的内存管理会有点困难,但SendMessageW() 可用于跨线程发送消息,因此从 Go 到 C 的基本通信将起作用。 (在开始 libui 重写之前,我打算用我的包 ui 执行此操作。我可能仍会这样做。)
  • 您没有继续处理您之前的问题并回复runtime.LockOSThread() 没有帮助您,而是发布了一个包含一般性和不清楚的问题的新问题,然后继续讨论您之前的问题的问题?这就是上一篇文章的目的!至于您对使用工具的问题:(1)一旦您被阻止,请附加一个调试器; (2)一旦你被阻止就生成一个转储; (3) 使用 Process Explorer / Process Hacker 查看你被阻塞时线程在做什么; (4) 在分析器下运行你的程序; (5) ...

标签: winapi go


【解决方案1】:

如果您使用CreateThread() 例程运行一个新的操作系统线程,Go 的调度程序将不会接触该线程。但是,您必须实现该线程与 Goroutine 通信的方法。例如,您不能直接从CreateThread() 创建的线程调用 Go 方法。相反,您将不得不使用一些基于 C 的系统来轮询 Goroutine 中的事件。

顺便说一句,如果你想从主操作系统线程运行循环,你应该在init()而不是main()中调用LockOSThread()。见https://github.com/golang/go/wiki/LockOSThread

func init() {
    runtime.LockOSThread();
}
func main() {
    // Run loop here.
}

【讨论】:

  • 感谢您的回答。在init()函数和main()函数中调用LockOSThread()有什么区别?如果我在main() 函数的开头调用LockOSThread(),调用goroutine 不会被锁定到操作系统线程吗?
  • 在 init 中使用 LockOSThread() 可确保 main() 从主 OS 线程运行。通常,特定于操作系统的 API(如 runloops)只有在从主线程调用时才能正常工作。我不知道 windows runloop 是否是这种情况——可能不是。
  • 真的晚了,但为了 Google 员工的利益:在明确记录这样做之前,runtime.LockOSThread() 不保证任何事情,即使在 init() 中运行也是如此。当然,Windows 并不关心您在 哪个 线程中执行特定于线程的工作,只要您选择一个即可。 (OS X 是一个要求运行循环位于操作系统创建的第一个线程上的操作系统示例。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-07
  • 2020-04-07
  • 1970-01-01
  • 2016-10-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多