专栏首页码农桃花源极端情况下收缩 Go 进程的线程数

极端情况下收缩 Go 进程的线程数

在 Go 的 runtime 里有一些创建了就没法回收的东西。

之前在 这篇 里讲过 allgs 没法回收的问题。

除了 allgs 之外,当前 Go 创建的线程也是没法退出的,比如这个来自 xiaorui.cc 的例子,我简单做了个修改,能从网页看到线程:

package main


/*
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void output(char *str) {
    usleep(1000000);
    printf("%s\n", str);
}
*/
import "C"
import "unsafe"


import "net/http"
import _ "net/http/pprof"


func init() {
 go http.ListenAndServe(":9999", nil)
}


func main() {
    for i := 0;i < 1000;i++ {
        go func(){
            str := "hello cgo"
            //change to char*
            cstr := C.CString(str)
            C.output(cstr)
            C.free(unsafe.Pointer(cstr))


        }()
    }
    select{}
}

可见 Goroutine 退出了,历史上创建的线程也是不会退出的。之前我也一直认为没有办法退出这些线程,不过这周被同事教育,还是有办法的。参考官方 issue 14592。文末有链接。

虽然问题直到现在依然没解决,但是这个 issue 里也提供了一种邪道解决办法,直接调用 LockOSThread,而不调用 Unlock,这样在退出的时候和当前 g 绑定的线程就会直接销毁:

把开头的程序改改,增加一个接口,killThread。

package main


/*
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void output(char *str) {
    usleep(1000000);
    printf("%s\n", str);
}
*/
import "C"


import (
 "net/http"
 "unsafe"


 "log"
 _ "net/http/pprof"
 "runtime"
 "sync"
)


func init() {
 go http.ListenAndServe(":9999", nil)
}


func main() {
 for i := 0; i < 1000; i++ {
  go func() {
   str := "hello cgo"
   //change to char*
   cstr := C.CString(str)
   C.output(cstr)
   C.free(unsafe.Pointer(cstr))


  }()
 }
 killThreadService()
 select {}
}


func sayhello(wr http.ResponseWriter, r *http.Request) {
 KillOne()
}


func killThreadService() {
 http.HandleFunc("/", sayhello)
 err := http.ListenAndServe(":10003", nil)
 if err != nil {
  log.Fatal("ListenAndServe:", err)
 }
}


// KillOne kills a thread
func KillOne() {
 var wg sync.WaitGroup
 wg.Add(1)


 go func() {
  defer wg.Done()
  runtime.LockOSThread()
  return
 }()


 wg.Wait()
}


启动后,发现创建了 1k+ 线程,curl localhost:10003,可以发现线程数在逐渐降低。

[1] https://github.com/golang/go/issues/14592

本文分享自微信公众号 - 码农桃花源(CoderPark),作者:曹春晖

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-09-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Go http client 连接池不复用的问题

    当 http client 返回值为不为空,只读取 response header,但不读 body 内容就执行 response.Body.Close(),那...

    梦醒人间
  • 偷天换日 —— g0 栈和用户栈如何完成切换?(四)

    上一讲讲完了 main goroutine 的诞生,它不是第一个,算上 g0,它要算第二个了。不过,我们要考虑的就是这个 goroutine,它会真正执行用户代...

    梦醒人间
  • 曹大谈内存重排

    写这篇文章的原因很简单,公司内部的 Golang 社区组织了第一期分享,主讲嘉宾就是我们敬爱的曹大。这个必定是要去听的,只是曹大的讲题非常硬核,所以提前找他要了...

    梦醒人间
  • 极端情况下收缩 Go 进程的线程数

    除了 allgs 之外,当前 Go 创建的线程也是没法退出的,比如这个来自 xiaorui.cc 的例子,我简单做了个修改,能从网页看到线程:

    范蠡
  • 为什么建议你常阅读源码?

    自身智力一般,技术迭代又非常快,为不至于总处于入门水平,给自己定位是后端,进一步定位现阶段是 web后台开发。

    谢伟
  • 看一篇,学一篇,今日份的pandas,你该这么学!No.2

    对象的方法就是,你能干啥 你能随风奔跑 你能跳过泥坑 你能用手指打98K 你还能跳C哩C

    梦想橡皮擦
  • 使用swoole实现异步任务处理

    swoole内置的swoole_http_server正好非常适合处理这种业务,示例代码实现如下

    luxixing
  • [android] 手机卫士手机定位的原理

    获取LocationManager对象,通过getSystemService(LOCATION_SERVICE)

    陶士涵
  • C++ 和 Makefile 笔记

    比如工程目录下,将CPP文件放置在 src 目录下,H文件放在 header下,则makefile可以这样写

    tonglei0429
  • nodejs 不支持 typescript (...paramName:any[])剩余参数。变相支持方式。

    旺财的城堡

扫码关注云+社区

领取腾讯云代金券