Golang Go语言中菜鸟问一个关于 goroutine 和闭包的问题
Golang Go语言中菜鸟问一个关于 goroutine 和闭包的问题
看到这样一段代码,有大佬给解释下pointer receiver
对 goroutine 执行的影响,或者说解释下这段代码三个函数表现行为的具体原因,谢谢了🙏
playground 地址: https://play.golang.org/p/cKrC0oy7FP
package main
import ( “fmt” “time” )
type field struct { name string }
func (p *field) print() { fmt.Println(p.name) }
func TestClosure() {
data := []*field{{"one"}, {"two"}, {"three"}} for _, v := range data { go v.print() } time.Sleep(3 * time.Second)
}
func TestClosure1() {
data := []field{{"one"}, {"two"}, {"three"}} for _, v := range data { go v.print() } time.Sleep(3 * time.Second)
}
func TestClosure2() {
data := []*field{{"one"}, {"two"}, {"three"}} for _, v := range data { go func(){ v.print() }() } time.Sleep(3 * time.Second)
}
func main(){ TestClosure() fmt.Println("----") TestClosure1() fmt.Println("----") TestClosure2() }
更多关于Golang Go语言中菜鸟问一个关于 goroutine 和闭包的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
首先看 TestClosure2 ; 犯了常见错误,可以参考: https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables
应该改成:<br>func TestClosure2() {<br><br> data := []*field{{"one"}, {"two"}, {"three"}}<br><br> for _, v := range data {<br> go func(v *field){<br> v.print()<br> }(v)<br> }<br><br> time.Sleep(3 * time.Second)<br>}<br>
理解了 TestClosure2 之后来看 TestClose1。这里要理解 function receiver ;可以参考: https://tour.golang.org/methods/4。关键是这句话
>
With a value receiver, the Scale method operates on a copy of the original Vertex value. (This is the same behavior as for any other function argument.)
>
所以要实现同样的效果,可以改成:<br>func TestClosure1() {<br><br> data := []field{{"one"}, {"two"}, {"three"}}<br><br> for _, v := range data {<br> go func(v field){<br> v.print()<br> }(v)<br> }<br><br> time.Sleep(3 * time.Second)<br>}<br>
最后放 playground: https://play.golang.org/p/2C71XfJm2SI
更多关于Golang Go语言中菜鸟问一个关于 goroutine 和闭包的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
#1
TestClosure1 也可以改成:<br>func TestClosure1() {<br><br> data := []field{{"one"}, {"two"}, {"three"}}<br><br> for i := range data {<br> go data[i].print()<br> }<br><br> time.Sleep(3 * time.Second)<br>}<br>
实现同样的效果
代码改成这样你就能明白了go<br>package main<br><br>import (<br> "fmt"<br> "time"<br>)<br><br>type field struct {<br> name string<br>}<br><br>func print(p *field) {<br> fmt.Println(<a target="_blank" href="http://p.name" rel="nofollow noopener">p.name</a>)<br>}<br><br>func TestClosure() {<br><br> data := []*field{{"one"}, {"two"}, {"three"}}<br><br> for _, v := range data {<br> go print(v)<br> }<br><br> time.Sleep(3 * time.Second)<br>}<br><br>func TestClosure1() {<br><br> data := []field{{"one"}, {"two"}, {"three"}}<br><br> for _, v := range data {<br> go print(&v)<br> }<br><br> time.Sleep(3 * time.Second)<br>}<br><br>func TestClosure2() {<br><br> data := []*field{{"one"}, {"two"}, {"three"}}<br><br> for _, v := range data {<br> go func(){<br> print(v)<br> }()<br> }<br><br> time.Sleep(3 * time.Second)<br>}<br><br>func main(){<br> TestClosure()<br> fmt.Println("----")<br> TestClosure1()<br> fmt.Println("----")<br> TestClosure2() <br>}<br><br>
go print(v)
会在执行函数前把参数(v)以值拷贝的方式固定住go func(){print(v)}
会在执行的时候再获取参数(v)的值
for 语句的循环变量,需要传入闭包时,应该先复制一份,因为每次循环都是使用同一个变量的
加 v := v 就可以复制了
TestClosure,指针
TestClosure1,v.print() 等价 (&v).print,print 使用的是相同的地址,但指针内容在循环后被改变
TestClosure2, 闭包常见问题
https://golang.org/ref/spec#Go_statements<br>The function value and parameters are evaluated as usual in the calling goroutine, but unlike with a regular call, program execution does not wait for the invoked function to complete. <br>
试试把 time.Sleep(1 * time.Second)加到执行 go 语句的后面看看
在Go语言中,goroutine和闭包是两个非常重要的概念,它们各自在不同的场景下发挥着关键作用。
首先,goroutine是Go语言的一种轻量级线程,由Go运行时管理。相比于传统的操作系统线程,goroutine的创建和销毁成本非常低,这使得Go程序能够轻松地创建成千上万个goroutine来并发执行任务。当你需要并发处理任务时,只需在函数调用前加上go
关键字,Go运行时就会自动将这个函数调用安排在一个新的goroutine中执行。
而闭包则是Go语言中的一个函数值,它引用了其外部作用域中的变量。闭包允许你将数据和操作这些数据的函数捆绑在一起,形成一个独立的实体。这在很多场景下都非常有用,比如当你需要将某个函数作为参数传递给另一个函数,并且希望这个函数能够访问到它被定义时的某些变量时,就可以使用闭包。
对于goroutine和闭包的关系,你可以在一个goroutine中执行一个闭包,这样闭包中的代码就会在并发环境下执行。需要注意的是,由于goroutine是并发执行的,所以你需要小心处理闭包中引用的外部变量,以避免出现数据竞争等并发问题。
总的来说,goroutine和闭包都是Go语言中非常强大的特性,它们能够极大地提高程序的并发性能和灵活性。如果你刚开始学习Go语言,建议多动手实践,通过编写一些简单的并发程序和闭包示例来加深理解。