Golang在32位Windows系统中创建新OS线程失败及runtime.newosproc致命错误问题

Golang在32位Windows系统中创建新OS线程失败及runtime.newosproc致命错误问题 首先,我并非专业的开发人员或程序员,因此如果我的解释有任何不足之处,敬请谅解。我在运行用Golang编写的Syncthing时遇到了这个问题。

基本上,程序因以下错误而崩溃:

runtime: failed to create new OS thread (have 1192 already; errno=8)
Panic at 2020-10-28T21:25:20+09:00
fatal error: runtime.newosproc

runtime stack:
runtime.throw(0x120fa12, 0x11)
	runtime/panic.go:1116 +0x64
runtime.newosproc(0x17ecafc0)
	runtime/os_windows.go:794 +0x146
runtime.newm1(0x17ecafc0)
	runtime/proc.go:1829 +0xbd
runtime.newm(0x13e5b7c, 0x12c2c000, 0x4a7, 0x0)
	runtime/proc.go:1808 +0x81
runtime.startm(0x12c2c000, 0x1)
	runtime/proc.go:1965 +0xac
runtime.handoffp(0x12c2c000)
	runtime/proc.go:2004 +0x28c
runtime.retake(0x68d8ec28, 0x4587, 0x0)
	runtime/proc.go:4805 +0x171
runtime.sysmon()
	runtime/proc.go:4713 +0x275
runtime.mstart1()
	runtime/proc.go:1172 +0x9b
runtime.mstart()
	runtime/proc.go:1137 +0x51

Syncthing的完整恐慌日志可在 https://github.com/syncthing/syncthing/files/5452199/panic-20201028-212520.log 获取。

我已在Syncthing的问题跟踪器上报告了此问题,但开发人员表示这可能是Go运行时或操作系统的问题,因此我在此提问。

我猜测这可能与此处解释的32位Windows中每个进程2000个线程的限制有关,但我无法确认这一点。崩溃确实发生在Syncthing运行于Windows 10 Enterprise LTSB 2016 x86(即32位Windows)下的情况。

这个问题是可以在Syncthing或Go运行时中修复/预防的,还是在32位Windows下运行Go应用程序的固有限制?在上面链接的博客文章中,微软提到了调整代码以“挤入”更多线程,但我不知道这如何对应到用Go编写的代码。

参考: github .com/syncthing/syncthing/issues/7064 github .com/golang/go/issues/42253


更多关于Golang在32位Windows系统中创建新OS线程失败及runtime.newosproc致命错误问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang在32位Windows系统中创建新OS线程失败及runtime.newosproc致命错误问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这是一个典型的32位Windows系统线程限制问题。错误信息显示已经创建了1192个线程,而32位Windows每个进程默认限制为2000个线程(包括系统线程)。Go运行时需要创建新的OS线程来处理goroutine,但达到了系统限制。

主要原因是32位Windows的地址空间有限(2GB用户空间),每个线程需要分配栈空间(默认1MB),大量线程会快速耗尽地址空间。虽然可以通过编译选项调整,但根本解决方法是减少线程使用或迁移到64位系统。

以下是一些可能的代码调整方案:

  1. 减少GOMAXPROCS
func main() {
    // 在程序启动时设置最大CPU使用数
    runtime.GOMAXPROCS(2)
    // 其他初始化代码...
}
  1. 调整栈大小(编译时):
# 使用更小的栈大小编译
go build -ldflags "-s -w -extldflags=-Wl,--stack,1048576"
  1. 控制并发goroutine数量
// 使用工作池限制并发
type WorkerPool struct {
    maxWorkers int
    semaphore  chan struct{}
}

func NewWorkerPool(max int) *WorkerPool {
    return &WorkerPool{
        maxWorkers: max,
        semaphore:  make(chan struct{}, max),
    }
}

func (wp *WorkerPool) Execute(task func()) {
    wp.semaphore <- struct{}{}
    go func() {
        defer func() { <-wp.semaphore }()
        task()
    }()
}

// 使用示例
func main() {
    pool := NewWorkerPool(100) // 限制最多100个并发goroutine
    
    for i := 0; i < 1000; i++ {
        pool.Execute(func() {
            // 执行任务
        })
    }
}
  1. 监控线程使用
func monitorThreads() {
    for {
        var count int32
        runtime.ReadMemStats(&memStats)
        // 可以通过外部工具监控线程数
        time.Sleep(5 * time.Second)
    }
}

然而,这些方法都只是缓解措施。在32位Windows上,当应用程序需要大量并发时,最终还是会遇到这个限制。Syncthing作为文件同步工具,可能需要处理大量并发连接和文件操作,特别容易触发此限制。

根本解决方案是:

  1. 升级到64位Windows系统
  2. 如果必须使用32位系统,考虑减少Syncthing的并发操作数量
  3. 调整系统注册表增加线程限制(风险较高):
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Executive]
"AdditionalDelayedWorkerThreads"=dword:00000040
"AdditionalCriticalWorkerThreads"=dword:00000020

这个问题不是Syncthing或Go运行时的bug,而是32位Windows平台的固有架构限制。Go运行时只是达到了操作系统允许的最大线程数。在64位系统上,这个限制会高得多(通常受可用内存限制)。

回到顶部