当运行两个Go协程时出现死锁问题。

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

Deadlock when running two go routine

问题

我现在是你的中文翻译助手,以下是你要翻译的内容:

我现在在业余时间学习Golang,并尝试在线进行一些样例考试来测试我所学到的知识。我遇到了这个编程考试任务,但是我似乎无法使其正常运行,而是遇到了"fatal error: all goroutines are asleep - deadlock!"的错误。有人可以帮忙看看我在这里做错了什么吗?

func executeParallel(ch chan<- int, done chan<- bool, functions ...func() int) {
	ch <- functions[1]()
	done <- true
}

func exampleFunction(counter int) int {
	sum := 0
	for i := 0; i < counter; i++ {
		sum += 1
	}
	return sum
}

func main() {
	expensiveFunction := func() int {
		return exampleFunction(200000000)
	}

	cheapFunction := func() int {return exampleFunction(10000000)}

	ch := make(chan int)
	done := make(chan bool)

	go executeParallel(ch, done, expensiveFunction, cheapFunction)

	var isDone = <-done
	for result := range ch {
		fmt.Printf("Result: %d\n", result)

		if isDone {
			break;
		}
	}
}
英文:

Im studying Golang now on my freetime and I am trying sample exams online to test what i learned,
I came about this coding exam task but I cant seem to make it work/run without a crash,
im getting fatal error: all goroutines are asleep - deadlock! error, can anybody help what I am doing wrong here?

func executeParallel(ch chan&lt;- int, done chan&lt;- bool, functions ...func() int) {
	ch &lt;- functions[1]()
	done &lt;- true
}

func exampleFunction(counter int) int {
	sum := 0
	for i := 0; i &lt; counter; i++ {
		sum += 1
	}
	return sum
}

func main() {
	expensiveFunction := func() int {
		return exampleFunction(200000000)
	}

	cheapFunction := func() int {return exampleFunction(10000000)}

	ch := make(chan int)
	done := make(chan bool)

	go executeParallel(ch, done, expensiveFunction, cheapFunction)

	var isDone = &lt;-done
	for result := range ch {
		fmt.Printf(&quot;Result: %d\n&quot;, result)

		if isDone {
			break;
		}
	}
}

答案1

得分: 1

你的executeParallel函数在提供少于2个函数时会引发恐慌,并且只会运行第二个函数:

ch <- functions[1]() // 如果提供的函数少于2个,则运行时会引发恐慌

我认为它应该像这样:并行运行所有输入函数,并获取第一个结果。

for _, fn := range functions {
	fn := fn // 以便每次迭代/ goroutine 都能得到正确的值
	go func() {
		select {
		case ch <- fn():
			// 第一个(最快的工作线程)获胜

		default:
			// 其他工作线程的结果被丢弃(如果读取器尚未读取结果)
			// 这样可以确保我们不会泄漏 goroutine,因为读取器只从通道中读取一个结果

		}
	}()
}

因此,不需要done通道-因为我们只需要读取唯一(最快)的结果:

ch := make(chan int, 1) // 足够大以捕获一个结果-即使读取器尚未读取

executeParallel(ch, expensiveFunction, cheapFunction)

fmt.Printf("Result: %d\n", <-ch)

链接:https://play.golang.org/p/skXc3gZZmRn

英文:

Your executeParallel function will panic if less than 2 functions are provided - and will only run the 2nd function:

ch &lt;- functions[1]() // runtime panic if less then 2 functions

I think it should look more like this: running all input functions in parallel and grabbing the first result.

for _, fn := range functions {

	fn := fn // so each iteration/goroutine gets the proper value
	
	go func() {
		select {
		case ch &lt;- fn():
			// first (fastest worker) wins

		default:
			// other workers results are discarded (if reader has not read results yet)
			// this ensure we don&#39;t leak goroutines - since reader only reads one result from channel

		}
	}()
}

As such there's no need for a done channel - as we just need to read the one and only (quickest) result:

ch := make(chan int, 1) // big enough to capture one result - even if reader is not reading yet

executeParallel(ch, expensiveFunction, cheapFunction)

fmt.Printf(&quot;Result: %d\n&quot;, &lt;-ch)

https://play.golang.org/p/skXc3gZZmRn

答案2

得分: 0

package main

import "fmt"

func executeParallel(ch chan<- int, done chan<- struct{}, functions ...func() int) {
	// 只有在函数数量大于1时才执行第二个函数[1]。
	if len(functions) > 1 {
		ch <- functions[1]()
	}
	// 关闭done通道以通知for-select跳出循环并返回主函数。
	close(done)
}

// example返回[0..counter-1]的迭代次数
func example(counter int) int {
	sum := 0
	for i := 0; i < counter; i++ {
		sum += 1
	}
	return sum

	// 注意(SS):这个函数可以直接返回"counter-1",以避免上面不必要的计算。
}

func main() {
	var (
		cheap     = func() int { return example(10000000) }
		expensive = func() int { return example(200000000) }
		ch        = make(chan int)
		done      = make(chan struct{})
	)

	// executeParallel接受ch、done通道,后跟可变数量的函数,
	// 其中第二个即索引为1的函数在一个单独的goroutine中执行,
	// 然后将其发送到ch通道,然后由下面的for-select接收器接收。
	go executeParallel(ch, done, expensive, cheap)

	for {
		select {
		// 等待done通道被发送数据或关闭。
		case <-done:
			return
		// 从ch接收数据(如果有数据被发送到ch)。
		case result := <-ch:
			fmt.Println("Result:", result)
		}
	}
}

我已经对代码进行了注释,以便更容易理解。由于您没有提供实际问题,所以逻辑可能仍然有误。

英文:
package main

import &quot;fmt&quot;

func executeParallel(ch chan&lt;- int, done chan&lt;- struct{}, functions ...func() int) {
	// Only execute the second function [1], if available.
	if len(functions) &gt; 1 {
		ch &lt;- functions[1]()
	}
	// Close the done channel to signal the for-select to break and the main returns.
	close(done)
}

// example returns the number of iterations for [0..counter-1]
func example(counter int) int {
	sum := 0
	for i := 0; i &lt; counter; i++ {
		sum += 1
	}
	return sum

	// NOTE(SS): This function could just return &quot;counter-1&quot;
	// to avoid the unnecessary calculation done above.
}

func main() {
	var (
		cheap     = func() int { return example(10000000) }
		expensive = func() int { return example(200000000) }
		ch        = make(chan int)
		done      = make(chan struct{})
	)

	// executeParallel takes ch, done channel followed by variable
	// number of functions where on the second i.e., indexed 1
	// function is executed on a separated goroutine which is then
	// sent to ch channel which is then received by the for-select
	// reciever below i.e., &lt;-ch is the receiver.
	go executeParallel(ch, done, expensive, cheap)

	for {
		select {
		// Wait for something to be sent to done or the done channel
		// to be closed.
		case &lt;-done:
			return
		// Keep receiving from ch (if something is sent to it)
		case result := &lt;-ch:
			fmt.Println(&quot;Result:&quot;, result)
		}
	}
}

I have commented on the code so that it's understandable. As you didn't the actual question the logic could be still wrong.

huangapple
  • 本文由 发表于 2021年7月5日 00:34:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/68246830.html
匿名

发表评论

匿名网友

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

确定