简体   繁体   English

与Google Go中的频道共享资源

[英]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中的通道,但我可以使用类似于CPython的互斥或信号量。

**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: 您的代码存在一些问题:

  1. 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. 这是一个僵局,因为通道中没有要读取的内容。

  2. 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的计数器,并允许调用方等待它们完成某些任务。

  3. 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在缓冲区中恰好有一个额外的位置。

  4. 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM