当编译一组函数时出现错误。

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

Go - Error when compiling a group of functions

问题

我正在尝试实现一个非常简单的测试函数,以验证我对欧拉问题的解决方案的结果。

在下面的代码中,我创建了一个切片的映射,其中在索引0处调用返回整数的函数,在索引1处调用期望的结果。

package euler

import "testing"

func TestEulers(t *testing.T) {

    tests := map[string][]int{
        "Euler1": {Euler1(), 233168},
        "Euler2": {Euler2(), 4613732},
        "Euler3": {Euler3(), 6857},
        "Euler4": {Euler4(), 906609},
        "Euler5": {Euler5(), 232792560},
        "Euler6": {Euler6(), 25164150},
    }

    for key, value := range tests {
        if value[0] != value[1] {
            t.Errorf("%s\nExpected: %d\nGot:%d",
                key, value[0], value[1])
        }
    }
}

对于该映射,每个函数都正常工作,并返回我期望的结果,如果我逐个运行它们或者如果我注释掉其中一半的键/值。

例如,如果我使用以下代码注释掉上面的函数调用,测试将通过。

tests := map[string][]int{
    "Euler1": {Euler1(), 233168},
    // "Euler2": {Euler2(), 4613732},
    "Euler3": {Euler3(), 6857},
    "Euler4": {Euler4(), 906609},
    // "Euler5": {Euler5(), 232792560},
    // "Euler6": {Euler6(), 25164150},
} 

但是,如果我按照以下方式重新排列注释,测试将会出错。

tests := map[string][]int{
    //"Euler1": {Euler1(), 233168},
    "Euler2": {Euler2(), 4613732},
    "Euler3": {Euler3(), 6857},
    "Euler4": {Euler4(), 906609},
    //"Euler5": {Euler5(), 232792560},
    //"Euler6": {Euler6(), 25164150},
}

测试将会报错:

WARNING: DATA RACE
Write by goroutine 6:
  runtime.closechan()
      /private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/runtime/chan.go:295 +0x0
  github.com/alesr/project-euler.Euler2()
      /Users/Alessandro/GO/src/github.com/alesr/project-euler/euler.go:40 +0xd7
  github.com/alesr/project-euler.TestEulers()
      /Users/Alessandro/GO/src/github.com/alesr/project-euler/euler_test.go:9 +0x46
  testing.tRunner()
      /private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/testing/testing.go:456 +0xdc

Previous read by goroutine 7:
  runtime.chansend()
      /private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/runtime/chan.go:107 +0x0
  github.com/alesr/numbers.FibonacciGen.func1()
      /Users/Alessandro/GO/src/github.com/alesr/numbers/numbers.go:103 +0x59

Goroutine 6 (running) created at:
  testing.RunTests()
      /private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/testing/testing.go:561 +0xaa3
  testing.(*M).Run()
      /private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/testing/testing.go:494 +0xe4
  main.main()
      github.com/alesr/project-euler/_test/_testmain.go:54 +0x20f

Goroutine 7 (running) created at:
  github.com/alesr/numbers.FibonacciGen()
      /Users/Alessandro/GO/src/github.com/alesr/numbers/numbers.go:105 +0x60
  github.com/alesr/project-euler.Euler2()
      /Users/Alessandro/GO/src/github.com/alesr/project-euler/euler.go:27 +0x32
  github.com/alesr/project-euler.TestEulers()
      /Users/Alessandro/GO/src/github.com/alesr/project-euler/euler_test.go:9 +0x46
  testing.tRunner()
      /private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/testing/testing.go:456 +0xdc
==================
panic: send on closed channel

goroutine 36 [running]:
github.com/alesr/numbers.FibonacciGen.func1(0xc8200a01e0)
    /Users/Alessandro/GO/src/github.com/alesr/numbers/numbers.go:103 +0x5a
created by github.com/alesr/numbers.FibonacciGen
    /Users/Alessandro/GO/src/github.com/alesr/numbers/numbers.go:105 +0x61

goroutine 1 [chan receive]:
testing.RunTests(0x24d038, 0x2f7340, 0x1, 0x1, 0xf78401)
    /private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/testing/testing.go:562 +0xafa
testing.(*M).Run(0xc82004df00, 0x1ff0e8)
    /private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/testing/testing.go:494 +0xe5
main.main()
    github.com/alesr/project-euler/_test/_testmain.go:54 +0x210

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
    /private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/runtime/asm_amd64.s:1696 +0x1

goroutine 35 [runnable]:
github.com/alesr/strings.Flip(0xc8200727a0, 0x6, 0x0, 0x0)
    /Users/Alessandro/GO/src/github.com/alesr/strings/strings.go:33 +0x17e
github.com/alesr/project-euler.Euler4(0x1ac9)
    /Users/Alessandro/GO/src/github.com/alesr/project-euler/euler.go:73 +0x95
github.com/alesr/project-euler.TestEulers(0xc8200b6000)
    /Users/Alessandro/GO/src/github.com/alesr/project-euler/euler_test.go:11 +0x63
testing.tRunner(0xc8200b6000, 0x2f7340)
    /private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/testing/testing.go:456 +0xdd
created by testing.RunTests
    /private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/testing/testing.go:561 +0xaa4
exit status 2
FAIL    github.com/alesr/project-euler    0.022s

但是,我检查了每个函数,它们都按预期工作。
如果你想要查看Euler源代码或者numbersstrings包,可以访问它们。

在Euler2函数中,我使用defer语句关闭了从FibonacciGen接收的通道。

而在FibonacciGen中,我也使用了另一个defer语句来关闭同一个通道。

这似乎是我的第一个错误。我应该只有一个而不是两个语句来关闭通道,因为它们试图关闭同一个东西。这样正确吗?

其次(在这一点上我更加不确定),defer语句会阻止函数在主goroutine返回之前被调用,对吗?无论我是在main包中调用它还是在其他地方调用它?

此外,由于数据通过通道从FibonacciGen流向主函数。在FibonacciGen中关闭通道后,似乎我不需要通知主函数。但是,如果我在主函数中关闭通道,我需要通知FibonacciGen停止尝试向该通道发送数据。

英文:

I'm trying to implement a very simple test function to verify results coming from my solutions for Euler problems.

In the following code I've created a map of slices where on the index 0, I call the function which return a integer and on the index 1, the result I expect from that function.

package euler

import "testing"

func TestEulers(t *testing.T) {

	tests := map[string][]int{
		"Euler1": {Euler1(), 233168},
		"Euler2": {Euler2(), 4613732},
		"Euler3": {Euler3(), 6857},
		"Euler4": {Euler4(), 906609},
		"Euler5": {Euler5(), 232792560},
		"Euler6": {Euler6(), 25164150},
	}

	for key, value := range tests {
		if value[0] != value[1] {
			t.Errorf("%s\nExpected: %d\nGot:%d",
				key, value[0], value[1])
		}
	}
}

For that map, every function works fine and return the result I expect if I run one by one or if I comment, let's say, half part of those keys/values.

For example, if I call the the function above with these lines commented the test will PASS.

tests := map[string][]int{
	"Euler1": {Euler1(), 233168},
	// "Euler2": {Euler2(), 4613732},
	"Euler3": {Euler3(), 6857},
	"Euler4": {Euler4(), 906609},
	// "Euler5": {Euler5(), 232792560},
	// "Euler6": {Euler6(), 25164150},
} 

But if I arrange the comments on that next way, for example, the test wouldn't.

tests := map[string][]int{
		//"Euler1": {Euler1(), 233168},
		"Euler2": {Euler2(), 4613732},
		"Euler3": {Euler3(), 6857},
		"Euler4": {Euler4(), 906609},
		//"Euler5": {Euler5(), 232792560},
		// "Euler6": {Euler6(), 25164150},
	}

The test will give me an error:

    WARNING: DATA RACE
Write by goroutine 6:
  runtime.closechan()
      /private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/runtime/chan.go:295 +0x0
  github.com/alesr/project-euler.Euler2()
      /Users/Alessandro/GO/src/github.com/alesr/project-euler/euler.go:40 +0xd7
  github.com/alesr/project-euler.TestEulers()
      /Users/Alessandro/GO/src/github.com/alesr/project-euler/euler_test.go:9 +0x46
  testing.tRunner()
      /private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/testing/testing.go:456 +0xdc

Previous read by goroutine 7:
  runtime.chansend()
      /private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/runtime/chan.go:107 +0x0
  github.com/alesr/numbers.FibonacciGen.func1()
      /Users/Alessandro/GO/src/github.com/alesr/numbers/numbers.go:103 +0x59

Goroutine 6 (running) created at:
  testing.RunTests()
      /private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/testing/testing.go:561 +0xaa3
  testing.(*M).Run()
      /private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/testing/testing.go:494 +0xe4
  main.main()
      github.com/alesr/project-euler/_test/_testmain.go:54 +0x20f

Goroutine 7 (running) created at:
  github.com/alesr/numbers.FibonacciGen()
      /Users/Alessandro/GO/src/github.com/alesr/numbers/numbers.go:105 +0x60
  github.com/alesr/project-euler.Euler2()
      /Users/Alessandro/GO/src/github.com/alesr/project-euler/euler.go:27 +0x32
  github.com/alesr/project-euler.TestEulers()
      /Users/Alessandro/GO/src/github.com/alesr/project-euler/euler_test.go:9 +0x46
  testing.tRunner()
      /private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/testing/testing.go:456 +0xdc
==================
panic: send on closed channel

goroutine 36 [running]:
github.com/alesr/numbers.FibonacciGen.func1(0xc8200a01e0)
	/Users/Alessandro/GO/src/github.com/alesr/numbers/numbers.go:103 +0x5a
created by github.com/alesr/numbers.FibonacciGen
	/Users/Alessandro/GO/src/github.com/alesr/numbers/numbers.go:105 +0x61

goroutine 1 [chan receive]:
testing.RunTests(0x24d038, 0x2f7340, 0x1, 0x1, 0xf78401)
	/private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/testing/testing.go:562 +0xafa
testing.(*M).Run(0xc82004df00, 0x1ff0e8)
	/private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/testing/testing.go:494 +0xe5
main.main()
	github.com/alesr/project-euler/_test/_testmain.go:54 +0x210

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
	/private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/runtime/asm_amd64.s:1696 +0x1

goroutine 35 [runnable]:
github.com/alesr/strings.Flip(0xc8200727a0, 0x6, 0x0, 0x0)
	/Users/Alessandro/GO/src/github.com/alesr/strings/strings.go:33 +0x17e
github.com/alesr/project-euler.Euler4(0x1ac9)
	/Users/Alessandro/GO/src/github.com/alesr/project-euler/euler.go:73 +0x95
github.com/alesr/project-euler.TestEulers(0xc8200b6000)
	/Users/Alessandro/GO/src/github.com/alesr/project-euler/euler_test.go:11 +0x63
testing.tRunner(0xc8200b6000, 0x2f7340)
	/private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/testing/testing.go:456 +0xdd
created by testing.RunTests
	/private/var/folders/q8/bf_4b1ts2zj0l7b0p1dv36lr0000gp/T/workdir/go/src/testing/testing.go:561 +0xaa4
exit status 2
FAIL	github.com/alesr/project-euler	0.022s

But still, I checked every single function and they work just as expected.
You can access the Euler source code or the packages numbers and strings if you want.

At Euler2 function I have a defer statement to close the channel which is receiving from FibonacciGen.

And on FibonacciGen I do have another defer statement to close the same channel.

It seems that's the my first error. I should have just one and not two statements to close the channel, since they are trying to close the same thing. Is that correct?

Second (and here I'm even a little more unsure), the defer statement will prevent the function to be called until the main goroutine returns, right? Independently if I call it on the package main or not?

Plus, since the data is flowing through the channel from FibonacciGen to the main function. It seems for me, that if I close the channel at FibonacciGen I don't need to notify the main function. But If I close the channel on the main function I do have to notify FibonacciGen to stop trying to send to this channel.

答案1

得分: 1

在你的Euler2()函数中,你没有检查通道是否已关闭。一旦通道关闭,它就会变为非阻塞状态,因此它会尝试向一个已关闭的通道发送值。

如果你只运行Euler2()函数,你的程序可能会在向关闭的通道发送值之前退出。

英文:

In your Euler2() you don't check if the channel has been closed. Once it's closed it's unblocked, so it tries to send a value to a now closed channel.

If you only run Euler2() your program might just exit before you send the value to the closed channel.

答案2

得分: 0

谢谢大家。在你们的帮助下,我明白了我之前错误地关闭了通道。

现在代码可以正常工作了。

func Euler2() int {

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

    go numbers.FibonacciGen(c, done)

    sum := 0
    var f int

    for {
        f = <-c
        if f < 4000000 {
            if f%2 == 0 {
                sum += f
            }
        } else {
            close(done)
            return sum
        }
    }
}

func FibonacciGen(c chan int, done chan bool) {

    for {
        select {
        case <-done:
            return
        default:
            for i, j := 0, 1; ; i, j = i+j, i {
                c <- i
            }
        }
    }

}
英文:

Thank you all. With your help I could understand that I was closing the channel in the wrong way.

Now works correctly.

func Euler2() int {

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

	go numbers.FibonacciGen(c, done)

	sum := 0
	var f int

	for {
		f = &lt;-c
		if f &lt; 4000000 {
			if f%2 == 0 {
				sum += f
			}
		} else {
			close(done)
			return sum
		}
	}
}

func FibonacciGen(c chan int, done chan bool) {

	for {
		select {
		case &lt;-done:
			return
		default:
			for i, j := 0, 1; ; i, j = i+j, i {
				c &lt;- i
			}
		}
	}

}

huangapple
  • 本文由 发表于 2015年12月15日 06:32:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/34277857.html
匿名

发表评论

匿名网友

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

确定