Go f(…) 与 f(go func(){…}()) 的区别是什么?

huangapple go评论81阅读模式
英文:

Go f(...) versus : f(go func(){...}())

问题

我明白了,以下是翻译好的内容:

我是否可以假设在Go语言中,这两种形式始终是等价的?

func f() {
    // 做一些事情
}
go f()

func f() {
   go func(){
     // 做一些事情
   }()
}
英文:

Am I correct to assume that with the Go language, these two formulations are always equivalent ?

func f() {
    // Do stuff
}
go f() 

and



func f() {
   go func(){
     // do stuff
   }()
) 

答案1

得分: 1

问题基本上在评论中得到了回答,但是尽管在简单情况下这两个示例都可以完成相同的任务,但根据实际目标,可能会更喜欢其中一个。

评论中提到的一个优点是允许代码的使用者决定并发性,而不是由你(编写者)决定。我认为这个经验法则通常更受欢迎,特别是对于为他人编写包的人来说(即使可能是你自己团队中的其他人)。我还在“互联网上”看到过这个经验法则的提倡,我认为这是因为在 Go 的早期,人们只是因为并发特性可用而使用(和滥用)它们。例如,返回一个通道,从中接收值,而不仅仅是返回值。

另一个区别是,在第一个示例中,f() 可能无法关闭你希望在作为 goroutine 运行时可访问的变量,你必须将所有东西都作为参数传递给 f()。在第二个示例中,go func() {...} 中的匿名函数可以关闭 f() 中的某些内容。

我更喜欢第二种风格的一个例子是启动服务器。例如:

func (app *Application) start() {
	if app.HttpsServer != nil {
		go func() {
			err := app.HttpsServer.ListenAndServeTLS(
				app.Config.TLSCertificateFile,
				app.Config.TLSKeyFile)
			if err != nil && err != http.ErrServerClosed {
				// unexpected error
				log.Printf(log.Critical, "error with https server: %s", err)
			}
		}()
	}

	go func() {
		err := app.HttpServer.ListenAndServe()
		if err != nil && err != http.ErrServerClosed {
			// unexpected error
			log.Printf(log.Critical, "error with http server: %s", err)
		}
	}()
}

这里的意图是,在 main() 中配置和控制 Application,启动服务器(一个在 https 上,一个在 http 上),然后程序流返回到 main()。在我的具体情况中,main() 等待来自操作系统的信号,然后关闭服务器并退出。两个 goroutine 都关闭了 app 并且可以访问其中包含的数据。这是“好”还是“坏”...谁知道呢,但对我来说效果很好。

所以基本上...“这取决于”。

英文:

The question was basically answered in the comments, but although in the simple case both examples do the same thing, one may be preferred over the other depending on what the actual goal is.

One that the comments mention is allowing the user of your code to decide on concurrency vs you (the writer) deciding. I think this rule of thumb is generally preferred especially for people writing packages for others to use (even if perhaps the others are in your own team). I've also seen this rule of thumb espoused elsewhere on "the internet", and I think arose because in the early days of Go, people were using (and abusing) concurrency features just because they were available. For example, returning a channel from which you'd receive a value instead of just returning the value.

Another difference is that in the top example, f() may not be able to close on variables that you might want accessible when run as a goroutine--you'd have to pass everything into f() as a parameter. In the second example the anonymous function in go func() {...} could close over something in f().

One example where I prefer the second style is starting servers. For example:

func (app *Application) start() {
	if app.HttpsServer != nil {
		go func() {
			err := app.HttpsServer.ListenAndServeTLS(
				app.Config.TLSCertificateFile,
				app.Config.TLSKeyFile)
			if err != nil && err != http.ErrServerClosed {
				// unexpected error
				log.Printf(log.Critical, "error with https server: %s", err)
			}
		}()
	}

	go func() {
		err := app.HttpServer.ListenAndServe()
		if err != nil && err != http.ErrServerClosed {
			// unexpected error
			log.Printf(log.Critical, "error with http server: %s", err)
		}
	}()
}

Here the intention is that Application is configured and controlled in main(), the servers (one on https, one on http) are started and program flow returns to main(). In my specific case, main() waits for a signal from the OS then shuts down the servers and exits. Both goroutines close over app and have access to the data it contains. Is this "good" or "bad"...who knows, but it works well for me.

So essentially... "It depends".

huangapple
  • 本文由 发表于 2021年12月3日 02:37:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/70204816.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定