Getting error "expected ';', found 'go'syntax" while trying to run a function in separate goroutine

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

Getting error "expected ';', found 'go'syntax" while trying to run a function in separate goroutine

问题

这是我正在尝试编写的代码:

total, err := go buildRepository.CountBuildScreenshots(buildId, OrgId, userID)
if err != nil {
    fmt.Println(err)
}

CountBuildScreenshots函数返回一个整数和一个错误。

我的使用场景是,我有多个构建,我想对每个构建运行这个函数。这个函数在计算上是昂贵的,对于一个构建需要5秒的计算时间。因此,我想使用等待组(wait Groups)来实现。

for i := 0; i < len(builds); i++ {
    buildId := builds[i].BuildId
    buildRepository := NewBuildRepository(database.GetReadLTMSConn())
    total, err := go (buildRepository.CountBuildScreenshots(buildId, OrgId, userID))
    if err != nil {
        fmt.Println(err)
    }
    (builds[i]).TotalScreenshots = total
}
英文:

This is the code that I am trying to write ->

total ,err := go buildRepository.CountBuildScreenshots(buildId, OrgId, userID)
			if err!=nil {
				fmt.Println(err)
			}

Count Build Screenshots return an integer and an error.

My use case is that I have multiple builds for which I want to run this function. This function is computationally expensive and takes 5 seconds for computing for a single build. Hence I wanted to use wait Groups for the same.

for i := 0; i &lt; len(builds); i++ {
			buildId := builds[i].BuildId
			buildRepository := NewBuildRepository(database.GetReadLTMSConn())
			total ,err := go (buildRepository.CountBuildScreenshots(buildId, OrgId, userID))
			if err!=nil {
				fmt.Println(err)
			}
			(builds[i]).TotalScreenshots = total
			
		}

答案1

得分: 2

go关键字用于启动一个并发的goroutine。它不会等待goroutine完成(并发运行是goroutine的整个目的),因此它无法提供函数的返回值。

你需要找到另一种方法来返回goroutine的结果。你还需要一个同步机制来知道何时完成。channel是标准解决方案。

如果你拥有CountBuildScreenshots的实现,你可以直接更改该函数。我假设你不拥有该实现,因此我们将在另一个函数中包装CountBuildScreenshots以处理响应。我们还将创建一个结构体来保存两个返回值。

type countBuildScreenshotsResult struct {
   total int
   err error
}

现在我们可以创建一个用于返回该结构体的channel。当我们准备好使用结果时,我们将在该channel上等待。

results := make(chan countBuildScreenshotsResult)

现在我们可以调用我们的goroutine:

go func() {
   var result countBuildScreenshotsResult
   result.total, result.err = buildRepository.CountBuildScreenshots(buildId, OrgId, userID)
   results <- result
}()

在代码的稍后位置,我们从channel中收集goroutine的结果:

res := <-results
if res.err != nil {
   ...
} else {
   ...
}

只有当你在等待CountBuildScreenshots返回时有其他操作需要完成时,将该函数并发运行才有意义。如果你将立即等待结果,那么在goroutine中运行它对你没有任何帮助。只有当你有其他并发运行的操作时,goroutine才有意义。换句话说,如果你最终的代码类似于:

results := make(chan Result)
go func() {
   ...
   results <- result
}()
res := <- results

那么使用goroutine就没有意义了,你可以直接在同一个goroutine中运行该操作,因为它将在完成时被阻塞。

英文:

the go keyword starts a concurrent goroutine. It does not wait for the goroutine to complete (running concurrently is the whole point of a goroutine) and therefore it cannot provide the return value of the function.

You need to find a different way of returning the result of your goroutine. You alson need a synchronization mechanism so that you know when it is complete. A channel is the standard solution.

If you own the implementation of CountBuildScreenshots you can change that function directly. I'll assume you do not own that implementation, in which case we'll wrap CountBuildScreenshots in another function to handle the response. We'll also create a struct to hold both return values.

type countBuildScreenshotsResult struct {
   total int
   err error
}

Now we can create a channel that can be used to return that struct. We will wait on the channel when we're ready to consume the results.

results := make(chan countBuildScreenshotsResult)

Now we can invoke our goroutine:

go func() {
   var result countBuildScreenshotsResult
   result.total, result.err := buildRepository.CountBuildScreenshots(buildId, OrgId, userID)
   results &lt;- result
}()

Some time later in the code, we collect the result of the goroutine from the channel:

res := &lt;-results
if res.err != nil {
   ...
} else {
   ...
}

Running this function concurrently in its own goroutine only makes sense if you have other operations to complete while waiting for CountBuildScreenshots to return. If you will immediately wait for the results, then running in a goroutine does nothing for you. It's only if you have other operations that run concurrently, that goroutines make sense. In other words, if you end up with code like:

results := make(chan Result)
go func() {
   ...
   results &lt;- result
}()
res := &lt;- results

Then there's no point to the goroutine and you may as well run the operation in the same goroutine, as it will be blocked on completion anyway.

答案2

得分: 1

我会将你的函数包装在一个不返回任何内容但写入通道的函数中。然后你可以从该通道中读取并等待所有结果。

我将循环包装在另一个goroutine中,以便在所有写入完成后关闭通道。

type result struct {
	Count int
	Err   error
}

func main() {
    // 用于存放结果的通道
	c := make(chan result)
    
    // 顶层goroutine等待所有生成的goroutine完成,并关闭通道
	go func(c chan result) {
        // 使用WaitGroup来等待
		wg := &sync.WaitGroup{}

       // 并发地启动长时间运行的函数
       // 在它们自己的goroutine中
		for i := 0; i < 10; i++ {
			wg.Add(1)
			go func(c chan result) {
				defer wg.Done()
                total, err := count()
                // 将结果写入通道
				c <- result{total, err}
			}(c)
		}

        // 等待所有goroutine完成
		wg.Wait()
        // 重要:关闭通道
		close(c)
	}(c)
    

    // 在通道关闭之前,按顺序处理结果
	for r := range c {
		if r.Err != nil {
			fmt.Println(r.Err)
		} else {
			fmt.Println(r.Count)
		}
	}

}

// 模拟一个运行时间较长的函数
func count() (int, error) {
	r := rand.Intn(10)
	time.Sleep(time.Duration(r) * time.Second)
	return 1, nil
}
英文:

I would wrap your function in a function that doesn't return anything but writes to a channel. Then you can read from that channel an wait for all results.

I am wrapping the loop in another goroutine to be able to close the channel after all are done writing to it.

type result struct {
	Count int
	Err   error
}

func main() {
    // the channel for the results
	c := make(chan result)
    
    // the top level goroutine waits for all
    // spawned goroutines to be done and closes the channel
	go func(c chan result) {
        // waitgroup is used to wait
		wg := &amp;sync.WaitGroup{}

       // kick of the long running functions concurrently
       // in they own goroutine
		for i := 0; i &lt; 10; i++ {
			wg.Add(1)
			go func(c chan result) {
				defer wg.Done()
                total, err = count()
                // write the results to the channel
				c &lt;- result{total, err}
			}(c)
		}

        // wait until all are done
		wg.Wait()
        // important, close the channel
		close(c)
	}(c)
    

    // range over the results as they are coming in
    // until the channel is closed
	for r := range c {
		if r.Err != nil {
			fmt.Println(r.Err)
		} else {
			fmt.Println(r.Count)
		}
	}

}

// simulate a longer running function
func count() (int, error) {
	r := rand.Intn(10)
	time.Sleep(time.Duration(r) * time.Second)
	return 1, nil
}

huangapple
  • 本文由 发表于 2021年12月24日 04:23:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/70467067.html
匿名

发表评论

匿名网友

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

确定