去线程死锁错误 - 使用go例程的正确方法是什么?

时间:2014-05-08 00:12:37

标签: multithreading go

我正在编写一个程序,根据用户输入计算黎曼和。程序会将函数分成1000个矩形(是的,我知道我还没有得到那个数学)并总结它们并返回答案。我正在使用go例程计算1000个矩形,但我得到了一个

fatal error: all go routines are asleep - deadlock!

处理多个go例程的正确方法是什么?我一直在环顾四周,还没有看到一个类似于我案例的例子?我是新手,想要遵守标准。这是我的代码(如果你想看看它的典型用例是什么,它是可以运行的 - 但它确实会破坏)

package main

import "fmt"
import "time"

//Data type to hold 'part' of function; ie. "4x^2"
type Pair struct {
    coef, exp int
}

//Calculates the y-value of a 'part' of the function and writes this to the channel
func calc(c *chan float32, p Pair, x float32) {
    val := x

    //Raise our x value to the power, contained in 'p'
    for i := 1; i < p.exp; i++ {

        val = val * val
    }

    //Read existing answer from channel
    ans := <-*c

    //Write new value to the channel
    *c <- float32(ans + (val * float32(p.coef)))
}

var c chan float32    //Channel
var m map[string]Pair //Map to hold function 'parts'

func main() {

    c = make(chan float32, 1001) //Buffered at 1001
    m = make(map[string]Pair)
    var counter int
    var temp_coef, temp_exp int
    var check string
    var up_bound, low_bound float32
    var delta float32

    counter = 1
    check = "default"

    //Loop through as long as we have no more function 'parts'
    for check != "n" {

        fmt.Print("Enter the coefficient for term ", counter, ": ")
        fmt.Scanln(&temp_coef)

        fmt.Print("Enter the exponent for term ", counter, ": ")
        fmt.Scanln(&temp_exp)

        fmt.Print("Do you have more terms to enter (y or n): ")
        fmt.Scanln(&check)

        fmt.Println("")

        //Put data into our map
        m[string(counter)] = Pair{temp_coef, temp_exp}

        counter++
    }

    fmt.Print("Enter the lower bound: ")
    fmt.Scanln(&low_bound)

    fmt.Print("Enter the upper bound: ")
    fmt.Scanln(&up_bound)

    //Calculate the delta; ie. our x delta for the riemann sum
    delta = (float32(up_bound) - float32(low_bound)) / float32(1000)

    //Make our go routines here to add
    for i := low_bound; i < up_bound; i = i + delta {

        //'counter' is indicative of the number of function 'parts' we have
        for j := 1; j < counter; j++ {

            //Go routines made here
            go calc(&c, m[string(j)], i)
        }

    }

    //Wait for the go routines to finish
    time.Sleep(5000 * time.Millisecond)

    //Read the result?
    ans := <-c

    fmt.Print("Answer: ", ans)
}

1 个答案:

答案 0 :(得分:5)

它死锁,因为calc()main()函数都会在任何人写入之前从通道读取。

所以你最终会在每个(非主要的)例行程序中阻止:

ans := <-*c

等待其他人去例行程序向频道输入值。因此,他们都没有到达他们实际写入频道的下一行。 main()例程将阻止:

ans := <-c

每个人都在等待=死锁

使用缓冲频道

您的解决方案应该只有calc()函数写入通道,而main()可以在for-range循环中读取它,总结来自go-routines的值

您还需要为main()添加一种方法,以便知道何时不会有更多值到达,可能使用sync.WaitGroup(可能不是最好的,因为main isn不想等待,而是总结一下)或普通的柜台。

使用共享内存

有时它不一定是您需要的频道。拥有使用sync/atomic更新的共享值(原子添加对浮点数无效)锁定sync.Mutex也可以正常工作。