multithreading - 控制 OS 线程与 goroutine 的启动?
问题描述
Go 的一大优点是它拥有大量可用的轻量级线程(goroutines)。这开辟了一些有趣的战略机会,但在某些情况下,Go 往往会产生 OS 线程;默认情况下,这些通常限制为最多 10,000 个。如果你碰巧超过了这个数字,事情就会崩溃。
以下代码将我们的最大 OS 线程锁定为 5,然后尝试启动 10 个并发 goroutine。这些 goroutine 将尝试使用系统调用(在这种情况下,文件创建 / 写入 / 关闭),这往往意味着我们的 goroutine 将临时(并且专门)与 os 线程关联。一旦系统调用完成,我们的 goroutine 将再次成为一个不错的轻量级线程——很可能与其他 goroutine 共享一个 os 线程。
当然有一个简单的解决方案——不要那样写!事实上,如果你取出文件打开/写入/关闭,一切都会完美运行......但暂时假设,下面的代码结构代表了更大更卑鄙的东西,它不能真正改变很多:
package main
import (
"fmt"
"log"
"os"
"runtime/debug"
"strconv"
"sync"
)
func main() {
MaxThreads := 5
debug.SetMaxThreads(MaxThreads)
var fileChan []chan string
// Create 10 channels that we can use to send data to our file writers.
for i := 0; i < 10; i++ {
fileChan = append(fileChan, make(chan string))
}
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(n int, fChan chan string) {
threadWriter(n, fChan)
wg.Done()
}(i, fileChan[i])
}
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
fileChan[i] <- "Test write " + strconv.Itoa(j) + " to chan " + strconv.Itoa(i)
}
}
for i := 0; i < 10; i++ {
close(fileChan[i])
}
wg.Wait()
fmt.Println("All done - success")
}
func threadWriter(i int, dataChan chan string) {
// Open the file
f, err := os.Create("tmp/Thread-" + strconv.Itoa(i) + ".txt")
if err != nil {
log.Fatal("Cannot open thread file", i)
return
}
// Wait for data to come in.
for str := range dataChan {
f.WriteString(str + "\n")
}
f.Close()
}
那么问题来了:(threadtest.go) 我将运行程序 20 次。有时它会正常工作,有时它会在运行时崩溃:程序超过 5 线程限制致命错误:线程耗尽
$ go build
$ if [ ! -d tmp ]; then mkdir tmp; fi; for i in `seq 1 20`; do ./threadtest 2>/dev/null | grep success > /dev/null; if [ $? -eq 0 ]; then echo "all good"; else echo "NOPE - Threads exhausted"; fi; rm tmp/Thread-*.txt; done
NOPE - Threads exhausted
all good
NOPE - Threads exhausted
all good
all good
all good
NOPE - Threads exhausted
NOPE - Threads exhausted
NOPE - Threads exhausted
all good
NOPE - Threads exhausted
all good
all good
all good
all good
all good
all good
all good
all good
all good
有时有效,有时无效——这仅取决于系统调用的时间,以及当时处于“操作系统线程”状态的 goroutine 的数量。这很酷..操作系统线程必须有限制。
然而,与其恐慌,如果有某种方法可以检测我们是否即将超过极限并旋转,那可能会正常......即:
func threadWriter(i int, dataChan chan string) {
while(debug.OsThreadsActive() > 9999) {
// spin here until we are safe
}
// Open the file
f, err := os.Create("tmp/Thread-" + strconv.Itoa(i) + ".txt")
if err != nil {
log.Fatal("Cannot open thread file", i)
return
}
// Wait for data to come in.
for str := range dataChan {
f.WriteString(str + "\n")
}
f.Close()
}
不幸的是,查看 runtime/proc.go,似乎没有一个简单的接口可以从中获取数据。mcount() 不是我可以查询的(实际上也不安全)。
那么 - 有没有人有任何建议,给定超过 MaxThreads 的 goroutine 计数,以及在这些 goroutines 中使用潜在阻塞 os 调用的要求,以尝试停止访问 MaxThreads?或者,我是否只需要在我的 goroutine 中的任何潜在系统调用周围放置一个数量有限的互斥锁,然后尝试代表 Go 限制并发线程?
解决方案
推荐阅读
- flutter - RenderFlex children having issue of unbounded height constraints
- cloud - 如何读取 xyzrgb 文件并将 rgb 用于 MeshLab 的颜色
- python - 读取文件并将文件中每个单词的首字母大写的程序
- python - 将字典添加到python中的列表不起作用
- r - understanding output of "order" function in R
- maven - Maven Release: How to skip deploy step?
- javascript - Web development: How to 'store' pages in the browsers and prevent them from reloading every time when navigating between them
- python - Webscraping inconsistently built tables using BeautifulSoup [gurufocus site]
- java - Which is the class of Spring Boot 404 error?
- java - Student here, Problem While loop doesn't work