[英]Shared resources with channels in google go
I am taking a look at the Google Go
language as I am building a realtime system, and I find the sharing of resources through channels a bit confusing. 在构建实时系统时,我正在研究
Google Go
语言,并且发现通过渠道进行资源共享有点令人困惑。 I'm trying for the sake of understanding, to let to different goroutines
to increment and decrement a shared value the same number of times, ending up at 0. I do know my code is wrong, but I'm not really getting the hang of it. 为了理解起见,我正在尝试让不同的
goroutines
在相同的次数上递增和递减一个共享值,最终以0结尾。它的。 Anybody care to explain what's wrong here? 有人在乎解释这里出了什么问题吗?
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)
}
I could use a mutex or a semaphore similar to what I would do using C
or Python
, although I want to take advantage of the channels in Go
. 尽管我想利用
Go
中的通道,但我可以使用类似于C
或Python
的互斥或信号量。
**UPDATE
**更新
Would adding a WaitGroup
change the program flow? 添加
WaitGroup
改变程序流程吗? I added a WaitGroup
, and it worked well. 我添加了一个
WaitGroup
,并且效果很好。 Although, I added the Done()
function after the whole for loop, will then the whole increment
run before decrement
? 虽然,我在整个for循环之后添加了
Done()
函数,然后整个increment
将在decrement
之前运行吗? I kind of want them to go 'in parallel' as far as it can, I know that only one routine can access I, but I want them to run independent of each other. 我有点希望它们尽可能“并行”运行,我知道只有一个例程可以访问我,但是我希望它们彼此独立运行。
There are a few problems with your code: 您的代码存在一些问题:
Both goroutines try to read from the channel at the same time. 两个goroutine都尝试同时从通道读取。 This is a deadlock as there is nothing in the channel to read.
这是一个僵局,因为通道中没有要读取的内容。
Println(<-c)
reads one value from the channel, not a result. Println(<-c)
从通道读取一个值,而不是结果。 It might read a result if you waited for both goroutines to finish, but that requires adding a WaitGroup
. 如果您等待两个goroutine完成,则可能会读取结果,但这需要添加
WaitGroup
。 a Waitgroup is like a semaphore allowing each goroutine to decrement a counter of pending goroutines, and allowing the caller to wait for them to finish some task. Waitgroup就像一个信号量,它允许每个goroutine减少待处理goroutine的计数器,并允许调用方等待它们完成某些任务。
Since sending blocks if there is no reader and reading is blocking if there's no sender, and you're a. 因为如果没有阅读器则发送阻塞,而如果没有发送者,则读取阻塞,而您就是。 waiting for both goroutines to finish first and b.
等待两个goroutine都先完成,然后b。 doing one more read than writes (the Println read), you need a
buffered channel
, that has exactly one extra place in the buffer. 要进行比写入更多的读取(Println读取),您需要一个
buffered channel
,该buffered channel
在缓冲区中恰好有一个额外的位置。
You need to push an initial value in the channel for the process to start. 您需要在通道中推送一个初始值以启动过程。
I've changed your code a bit and this example now works (although notice that it's not realy increment->decrement->increment->.... but rathter increment->increment->...->decrement->decrement->.... until we're done. 我已经对您的代码进行了一些更改,并且此示例现在可以正常工作(尽管请注意,它并不是真正的增量->减量->增量-> ....,但更复杂的是增量->增量-> ...->减量->减量-> ....直到完成。
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 )
}
Here is a complete example of the kind of shared state engine that I think you are talking about 这是我认为您在谈论的那种共享状态引擎的完整示例
Note use of WaitGroup
as you suggested in your edit to synchronise the two channels. 请注意,如您在编辑中建议的那样使用
WaitGroup
来同步两个通道。
PS don't use import . "fmt"
PS不使用
import . "fmt"
import . "fmt"
it is considered to be bad practice. 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")
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.