我正在构建一个实时系统,看看Google Go
语言,我发现通过渠道共享资源有点令人困惑。我正在尝试为了理解,让不同的goroutines
递增和递减共享值相同的次数,最后为0.我知道我的代码是错的,但我不是真的得到它的悬念。有人在乎解释这里有什么问题吗?
package main
import (
. "fmt"
. "runtime"
)
func increment(c chan int) {
for x := 0; x < 10; x++ {
a := <-c
a++
c <- a
}
}
func decrement(c chan int) {
for x := 0; x < 10; x++ {
a := <-c
a--
c <- a
}
}
func main() {
GOMAXPROCS(NumCPU())
c := make(chan int)
go increment(c)
go decrement(c)
Println(<-c)
}
我可以使用类似于我使用C
或Python
执行操作的互斥锁或信号量,但我想利用Go
中的频道。
**更新
添加WaitGroup
会改变程序流程吗?我添加了一个WaitGroup
,效果很好。虽然我在整个for循环之后添加了Done()
函数,然后整个increment
会在decrement
之前运行吗?我希望他们尽可能“并行”,我知道只有一个例程可以访问我,但我希望它们彼此独立运行。
答案 0 :(得分:2)
您的代码存在一些问题:
两个goroutines都试图同时从频道中读取。这是一个死锁,因为通道中没有任何内容可供阅读。
Println(<-c)
从频道中读取一个值,而不是结果。如果您等待两个goroutine完成,它可能会读取结果,但这需要添加WaitGroup
。 Waitgroup就像一个信号量,允许每个goroutine减少挂起的goroutine的计数器,并允许调用者等待它们完成某项任务。
如果没有读者就发送阻止,如果没有发送者则读取被阻止,并且你是一个。等待两个goroutines先完成和b。做一次读取而不是写入(Println读取),你需要一个buffered channel
,它在缓冲区中只有一个额外的位置。
您需要在渠道中提取初始值才能启动流程。
我已经改变了你的代码,这个例子现在有效(虽然注意它不是真正的增量 - >减少 - >增量 - > ...但是rathter increment-&gt; increment-&gt;。 ..-&gt;减量 - &gt;减量 - &gt; ....直到我们完成。
package main
import (
. "fmt"
. "runtime"
"sync"
)
func increment(c chan int, wg *sync.WaitGroup) {
for x := 0; x < 10; x++ {
a := <-c
Println("increment read ", a)
a++
c <- a
}
Println("Incrment done!")
wg.Done()
}
func decrement(c chan int, wg *sync.WaitGroup) {
for x := 0; x < 10; x++ {
a := <-c
Println("Decrement read ", a)
a--
c <- a
}
Println("Dencrment done!")
wg.Done()
}
func main() {
GOMAXPROCS(NumCPU())
//we create a buffered channel with 1 extra space. This means
//you can send one extra value into it if there is no reader, allowing for the final result to be pushed to println
c := make(chan int, 1)
//we create a wait group so we can wait for both goroutines to finish before reading the result
wg := sync.WaitGroup{}
wg.Add(1) //mark one started
go increment(c, &wg)
wg.Add(1) //mark another one started. We can just do Add(2) BTW
go decrement(c, &wg)
//now we push the initial value to the channel, starting the dialog
c <- 0
//let's wait for them to finish...
wg.Wait()
//now we have the result in the channel's buffer
Println("Total: ", <-c )
}
答案 1 :(得分:1)
Here is a complete example我认为你正在谈论的那种共享状态引擎
请注意使用编辑中建议的WaitGroup
同步两个频道。
PS不使用import . "fmt"
它被认为是不好的做法。
package main
import (
"fmt"
"runtime"
"sync"
)
// Commands for the engine
const (
INC = iota
DEC
ANSWER
QUIT
)
// Engine which takes commands and acts on some shared state
func engine(c chan int, reply chan int) {
counter := 0
for {
switch <-c {
case INC:
counter++
case DEC:
counter--
case ANSWER:
reply <- counter
case QUIT:
reply <- counter
return
}
}
}
// Add n times then signal done via the waitgroup
func increment(n int, c chan int, wg *sync.WaitGroup) {
defer wg.Done()
for x := 0; x < n; x++ {
c <- INC
}
}
// Subtract n times then signal done
func decrement(n int, c chan int, wg *sync.WaitGroup) {
defer wg.Done()
for x := 0; x < n; x++ {
c <- DEC
}
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
// Start the engine
c := make(chan int)
reply := make(chan int)
go engine(c, reply)
// Do adding and subtracting and wait for them to finish
wg := new(sync.WaitGroup)
wg.Add(2)
go increment(101, c, wg)
go decrement(100, c, wg)
wg.Wait()
// Read the answer
c <- ANSWER
fmt.Printf("Total is %d\n", <-reply)
// Stop the engine
c <- QUIT
<-reply
fmt.Printf("All done\n")
}