Go 去打进程的最大线程数?

Go 去打进程的最大线程数?,go,Go,我正在尝试Go做一些文件系统使用分析,我希望通过将几乎所有内容作为goroutine生成,并依靠Go VM(和GOMAXPROCS)来管理,从而尽可能快地生成代码。我一直看着这段代码运行(相当快),直到它死掉。我检查了top,它列出了我的进程有1500个线程 我想我可能已经达到了一些极限,因此在等待操作系统时进程陷入僵局。我检查了我的操作系统(FreeBSD)限制,果然它被列为每个进程最多1500个线程 令人惊讶的是,我检查了Go文档,它说GOMAXPROCS只是对运行线程的限制,但阻塞的线程不

我正在尝试Go做一些文件系统使用分析,我希望通过将几乎所有内容作为goroutine生成,并依靠Go VM(和GOMAXPROCS)来管理,从而尽可能快地生成代码。我一直看着这段代码运行(相当快),直到它死掉。我检查了top,它列出了我的进程有1500个线程

我想我可能已经达到了一些极限,因此在等待操作系统时进程陷入僵局。我检查了我的操作系统(FreeBSD)限制,果然它被列为每个进程最多1500个线程

令人惊讶的是,我检查了Go文档,它说GOMAXPROCS只是对运行线程的限制,但阻塞的线程不算在内

所以我的问题是:

  • 公平地说,我不能依靠GoVM作为一个全局池来防止达到这类操作系统的限制吗

  • 有没有一个惯用的方法来处理这个问题(好一点,这只是我使用围棋的第二天)

    • 特别是,我还没有找到一个很好的方法,除了同步关闭一个频道时,我使用它。有更好的办法吗

    • 我想抽象出样板文件(使用go例程和 关闭通道(完成时),是否有一种类型安全的方法在没有泛型的情况下执行此操作

以下是我当前的代码:

func AnalyzePaths(paths chan string) chan AnalyzedPath {
    analyzed := make(chan AnalyzedPath)
    go func() {
        group := sync.WaitGroup{}
        for path := range paths {
            group.Add(1)
            go func(path string) {
                defer group.Done()
                analyzed <- Analyze(path)
            }(path)
        }
        group.Wait()
        close(analyzed)
    }()
    return analyzed
}

func GetPaths(roots []string) chan string {
    globbed := make(chan string)
    go func() {
        group := sync.WaitGroup{}
        for _, root := range roots {
            group.Add(1)
            go func(root string) {
                defer group.Done()
                for _, path := range glob(root) {
                    globbed <- path
                }
            }(root)
        }
        group.Wait()
        close(globbed)
    }()
    return globbed
}

func main() {
    paths := GetPaths(patterns)
    for analyzed := range AnalyzePaths(paths) {
        fmt.Println(analyzed)
    }
}
func分析路径(路径chan字符串)chan分析路径{
已分析:=生成(chan AnalyzedPath)
go func(){
组:=sync.WaitGroup{}
对于路径:=范围路径{
组。添加(1)
go func(路径字符串){
延迟组。完成()
分析大约2个月前(或更多)语言开发人员谈到线程计数控制的入侵(以及一些其他限制)。因此,我们可以期待很快看到它。一个多月前,我开发了这个问题,并在我的linux计算机上发现GOMAXPROCS的值不超过256。如果我向它发送300或更多,结果总是256。但我发现goroutines不是线程。goroutines可以存在于一个线程中

至于惯用的同步——我认为没有必要同步太多。 在我的代码中,我通常使用goroutine只通过通道进行通信的思想,通道应该作为goroutine的参数传递

func main() {
    ch1 := make(chan SomeType1)
    ch2 := make(chan SomeType2)
    go generator(ch1, ch2)
    go processor(ch1, ch2)
    // here main func becomes waiting until it capture 2 of ch2-finished-signals 
    <- ch2
    <- ch2
    // usually we don't need the exact values of ch2-signals,
    // so we assign it to nothing 
}

func generator(ch1 chan SomeType1, ch2 chan SomeType2) {
    for (YOUR_CONDITION){
        // generate something
        //....
        // send to channel
        ch1 <- someValueOfType1
    }
    ch1 <- magicStopValue
    ch2 <- weAreFinishedSignal1
}

func processor(ch1 chan SomeType1, ch2 chan SomeType2) {
    // "read" value from ch1 
    value := <-ch1
    for value != magicStopValue {
        // make some processing
        // ....
        //get next value from ch1 and replay processing
        value = <- ch1
    }
    // here we can send signal that goroutine2 is finished
    ch2 <- weAreFinishedSignal2
}
func main(){
ch1:=制造(类型1)
ch2:=制造(类型2)
go发电机(ch1、ch2)
go处理器(ch1、ch2)
//在此,主func将一直等待,直到它捕获2个ch2完成信号

一些评论:a)没有Go虚拟机。有Go运行时,但这不是一个虚拟机或与之相近的东西。b)不,Go不是防止你滥用硬件的日托;实际上Go的级别很低,足以很好地使用你的硬件(但允许越界)。c)你说“通过生成几乎所有的goroutine尽可能快地生成代码”,这在我看来是有问题的:当然,您需要至少n个并发goroutine来保持n个处理器(核心)a)我们不要太迂腐,因为在这个问题上,虚拟机和运行时是可互换的,因为在我的代码和硬件之间有某种东西,可以为我管理它。b)现在你正在屈尊俯就……这是一种经常被吹捧为awesom的语言e对于并发性,智能资源管理在我看来并不是一个不合理的期望。c)Duh,但我不是在问为什么我没有获得更快的性能,我是在问我是否因为达到了操作系统的限制而陷入了死锁。a)不是迂腐:没有任何东西会在代码和硬件之间。操作系统和运行时更多地位于代码的一侧,而不是介于两者之间。b)和c)不会带来任何结果:你期望Go提供一些在任何情况下都无法提供的东西。@Volker,这是一种迂腐的行为,因为把VM换成运行时对我的问题没有影响。其次,Go有一个“调度器”"管理goroutines——对我来说,这确实表明,在goroutines方面,运行时并不是“坐在一边”。交付并非不可能。运行时可以检查是否存在操作系统限制,避免创建更多线程……它可以将GOMAXPROCS视为活动线程和阻塞线程,也可以添加GOMAXT线程选项。如果我遇到了我认为是的问题,上面的任何一项都很好,而且不是“不可能”。嗯,但是由于我的goroutines可能会无序执行,我不确定如何知道在不使用sync的情况下何时生成“weAreFinishedSignal”。如果我理解正确,您的示例不会显式地并行处理,但是可以产生n个处理器来实现并行化?嗯,这里有3个执行分支。主分支产生生成器分支和处理器分支,然后变得昏昏欲睡(我所做的等待信号是试图为我想要分析的每个路径生成一个新的goroutine,而这个设置是按每个处理器串行处理它们…如果我想要更多的并行化,我需要生成更多的处理器…但至少它设置了一个显式限制。