How concurrency works with anonymous functions ? go

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

How concurrency works with anonymous functions ? go

问题

我正在尝试遍历一个列表(例如SQL行),并为每一行触发goroutine。问题在于传递给函数的值在运行时不会被评估,因此根据函数执行所需的时间,它可能会使用下一行中的任何值而不是当前行中的值。

我知道我可以将函数提取为普通函数并传递参数,但我仍然想共享一些全局变量(以避免使用太多函数参数),因此需要使用匿名函数。
对我来说,匿名函数从环境中获取变量并在执行时使用它也很令人困惑,因为据我所了解,它应该在单独的goroutine中执行,就像Unix程序中的&一样,通信只通过通道进行。

问题是如何使匿名函数在运行时接收vc的副本并使用它?

package main

import "fmt"
import "time"


type mystruct struct {
    i int
    s string
}

func main() {

    vc := mystruct{}
    vc.i = 1
    vc.s = "hi"
    gl := "this is global"
    for i := 1; i < 4; i++ {

    go func() {
        vc.i++
        time.Sleep(1 * time.Second)
        
        fmt.Printf("row specific is %v, global is %v", vc, gl)
    }()

    }
    time.Sleep(2 * time.Second)

}

你可以在这里运行代码:点击这里

英文:

I'm trying to loop through a list (e.g. sql rows) and fire go routines for each row. The issue is that the values passed to the function are not evaluated at the runtime so depending by how much time the function takes to execute it may use whatever value is on one of the next(s) rows instead the current one.

I'm aware that I could extract the function in a normal one and pass the arguments but I still want to share some global variables(to avoid many function arguments) thus the need to use an anonymous function.
Still it's also confusing to me that the anonymous function takes variables from the environment while it's being executed because as far as I understand it is supposed to be executed in a separate routines just like & in unix programs, the communication being done only through channels.

The question is how do I make the anonymous function to receive a copy of vc and use it during runtime ?

package main

import &quot;fmt&quot;
import &quot;time&quot;


type mystruct struct {
	i int
	s string
}

func main() {

	vc := mystruct{}
	vc.i = 1
	vc.s = &quot;hi&quot;
    gl := &quot;this is global&quot;
	for i := 1; i &lt; 4; i++ {
	
	go func() {
		vc.i++
		time.Sleep(1 * time.Second)
		
		fmt.Printf(&quot;row specific is %v, global is %v&quot;, vc, gl)
	}()

	}
	time.Sleep(2 * time.Second)

}

Play here

答案1

得分: 2

代码正在从多个goroutine访问相同的结构体。
你需要使用互斥锁来保护结构体,以控制对其值的访问。
此外,你需要等待所有的goroutine完成。仅使用Time.Sleep无法保证这一点。使用WaitGroup。以下是一个可工作的示例:

package main

import "fmt"
import "time"
import "strconv"
import "sync"

type mystruct struct {
    i int
    s string
    m sync.Mutex
}

func main() {

    vc := mystruct{}
    vc.i = 1
    vc.s = "hi"

    var wg sync.WaitGroup

    for i := 0; i < 4; i++ {
        wg.Add(1)
        go func() {
            vc.m.Lock()
            defer vc.m.Unlock()
            defer wg.Done()
            vc.i++
            time.Sleep(1 * time.Millisecond)
            vc.s = "good bye" + strconv.Itoa(vc.i)
            fmt.Printf("%v \n", vc)
        }()

    }

    wg.Wait()

}

Playground: http://play.golang.org/p/K6rnQQSA1c

英文:

The code is accessing the same struct from multiple goroutines .
You need to protect the struct with a mutex to govern the access to its value.
Also you need to wait for all the goroutines to finish. Time.Sleep alone will not guarantee that. Use a Wait group. Here is a working example:

package main

import &quot;fmt&quot;
import &quot;time&quot;
import &quot;strconv&quot;
import &quot;sync&quot;

type mystruct struct {
	i int
	s string
	m sync.Mutex
}

func main() {

	vc := mystruct{}
	vc.i = 1
	vc.s = &quot;hi&quot;

	var wg sync.WaitGroup

	for i := 0; i &lt; 4; i++ {
		wg.Add(1)
		go func() {
			vc.m.Lock()
			defer vc.m.Unlock()
			defer wg.Done()
			vc.i++
			time.Sleep(1 * time.Millisecond)
			vc.s = &quot;good bye&quot; + strconv.Itoa(vc.i)
			fmt.Printf(&quot;%v \n&quot;, vc)
		}()

	}

	wg.Wait()

}

Playground: http://play.golang.org/p/K6rnQQSA1c

答案2

得分: 0

我真的不明白你想要实现什么,但是,如果你的唯一目标是复制变量,你可以将它传递给go函数,就像这样

for i := 1; i < 4; i++ {
	go func(vc mystruct) {
		vc.i++
		//time.Sleep(1 * time.Second)

		fmt.Printf("row specific is %v, global is %v\n", vc, gl)
	}(vc)
}
英文:

I honestly don't understand what you're trying to achieve, however, if your only goal is to make a copy of the variable, you can just pass it to the go func like this:

for i := 1; i &lt; 4; i++ {
	go func(vc mystruct) {
		vc.i++
		//time.Sleep(1 * time.Second)

		fmt.Printf(&quot;row specific is %v, global is %v\n&quot;, vc, gl)
	}(vc)
}

答案3

得分: -2

似乎唯一的方法是在匿名函数内部创建结构体并复制闭包的值。请参考下面的代码。正如@fabrizioM建议的那样,如果需要同步,应该使用wg。然而,问题不是关于同步的,因此我没有包括wg。

但我仍然不确定这样是否安全,因为vc的复制可能需要一些时间,所以仍然欢迎编辑/改进。

package main

import "fmt"
import "time"

type mystruct struct {
    i int
    s string
}

func main() {

    vc := mystruct{}
    vc.i = 1
    vc.s = "hi"

    for i := 1; i < 4; i++ {

        go func() {
            vc2 := vc
            vc.i++
            time.Sleep(1 * time.Second)

            fmt.Printf("%v\n", vc2)
        }()

    }
    time.Sleep(2 * time.Second)

}

点击播放

英文:

It seems the only way is to create the struct within the anonymous function and copy the closure value . See the code below. As @fabrizioM advised you should use wg if you need synchronisation. However the question was not about that thus I don't include the wg.

Still I'm not sure if it's safe as the the copy of vc may take some time so edits/ improvements are still welcome

package main

import &quot;fmt&quot;
import &quot;time&quot;

type mystruct struct {
	i int
	s string
}

func main() {

	vc := mystruct{}
	vc.i = 1
	vc.s = &quot;hi&quot;

	for i := 1; i &lt; 4; i++ {

		go func() {
			vc2 := vc
			vc.i++
			time.Sleep(1 * time.Second)

			fmt.Printf(&quot;%v\n&quot;, vc2)
		}()

	}
	time.Sleep(2 * time.Second)

}

Click to play

huangapple
  • 本文由 发表于 2014年7月3日 07:32:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/24542711.html
匿名

发表评论

匿名网友

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

确定