V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
ryanking8215
V2EX  ›  Go 编程语言

程序跑了一段时候后留下了好多操作系统线程,没多少 goroutine

  •  
  •   ryanking8215 ·
    ryanking8215 · 2017-11-27 14:14:16 +08:00 · 2071 次点击
    这是一个创建于 2564 天前的主题,其中的信息可能已经有所发展或是发生改变。

    程序是调个 c 库实现文件收发,并将传输中的状态通过 http 发送给另外的监控服务。 发现测试过一段时候以后,会有很多的线程,但是用 dlv 调试看没多少 goroutines,而且日志也显示为传输开的 goutine 都退出了。昨天测试一下,竟然开了 1000 多个线程。

    以下是其中一个 thread 的 stack:

    (dlv) bt
    0  0x000000000046df03 in runtime.futex
       at /home/vagrant/resource/go/src/runtime/sys_linux_amd64.s:388
    1  0x0000000000437e92 in runtime.futexsleep
       at /home/vagrant/resource/go/src/runtime/os_linux.go:45
    2  0x000000000041e042 in runtime.notesleep
       at /home/vagrant/resource/go/src/runtime/lock_futex.go:145
    3  0x000000000044036d in runtime.stopm
       at /home/vagrant/resource/go/src/runtime/proc.go:1594
    4  0x0000000000441178 in runtime.findrunnable
       at /home/vagrant/resource/go/src/runtime/proc.go:2021
    5  0x0000000000441cec in runtime.schedule
       at /home/vagrant/resource/go/src/runtime/proc.go:2120
    6  0x0000000000442063 in runtime.park_m
       at /home/vagrant/resource/go/src/runtime/proc.go:2183
    7  0x0000000000469f1b in runtime.mcall
       at /home/vagrant/resource/go/src/runtime/asm_amd64.s:240
    

    通过网络了解了一些 go 的调度器的知识,也看了上述相关的代码,没发现有什么异常,就是 thread(即所谓的 m)本身退出过程是正确的,应该会 futex_wait,等待下一次调度。 但貌似之后有 goroutine 都不会再使用这个 m 来调度,需要新开线程来执行,所以造成线程越来越多。

    go 版本是 1.7.1, 有没有大侠能够指点一下迷津,谢谢。

    11 条回复    2017-11-28 12:43:28 +08:00
    cloudzhou
        1
    cloudzhou  
       2017-11-27 14:16:42 +08:00
    你不是用 c 的库,直接使用 Go 开发呢?
    Carseason
        2
    Carseason  
       2017-11-27 14:16:45 +08:00 via iPhone
    go 会等到一定程度的时候会回收的吧
    ryanking8215
        3
    ryanking8215  
    OP
       2017-11-27 14:19:00 +08:00
    @cloudzhou c 库实现了网络加速部分,go 建立上层的产品逻辑。
    @Carseason 没回收,上周五出现的,周一来看还是 1000 多个线程,周末没有测试。
    realityone
        4
    realityone  
       2017-11-27 14:23:14 +08:00 via iPhone
    检查你 go 调 c 的代码
    是不是调一次就多一个不会回收的线程
    ryanking8215
        5
    ryanking8215  
    OP
       2017-11-27 16:25:48 +08:00
    @realityone 也不是,几天也测了好久,但是线程数没有增加,还是 1018 个。感觉像某些情况下被触发,不是每次都是。另外 c 代码里也 check 了下,没发现线程泄露。
    owenliang
        6
    owenliang  
       2017-11-27 17:00:09 +08:00
    很明显是调用 C 库重复,也许对 cgo 的理解不够?
    lizon
        7
    lizon  
       2017-11-27 17:03:25 +08:00
    C 库里会启动线程?
    建议了解 runtime.GOMAXPROCS()
    zts1993
        8
    zts1993  
       2017-11-27 17:09:14 +08:00   ❤️ 1
    cgo 如果没有记错的话和长时间 syscall 一样会启动新线程执行。
    xiaxiaocao
        9
    xiaxiaocao  
       2017-11-27 17:16:01 +08:00   ❤️ 2
    cgo 调用并发多的时候会开更多的线程,但是现在 go 的实现线程空闲后不会退出,看这个 issue:
    https://github.com/golang/go/issues/14592
    lizon
        10
    lizon  
       2017-11-27 17:59:03 +08:00   ❤️ 1
    https://www.cockroachlabs.com/blog/the-cost-and-complexity-of-cgo/
    简单来说,对于 cgo 的并发调用会导致创建新的线程

    cgo 调用的执行流直接在系统线程上,而不是 goroutine 上,不受 go 调度器管理
    ryanking8215
        11
    ryanking8215  
    OP
       2017-11-28 12:43:28 +08:00
    @xiaxiaocao @lizon 感谢!

    应该就是这个问题。c 库通过回调通知事件,回调里不清楚如何调 go,我用 pthread_cond_t 条件变量封装了一下,回调里 signal 这个 cond,在 go 里 wait 这个 cond。应该就是这个操作阻塞了线程,之后如果有并发任务会重新开线程,造成好多线程悬挂。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5421 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 09:06 · PVG 17:06 · LAX 01:06 · JFK 04:06
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.