与google中的频道共享资源

时间:2014-01-17 06:42:48

标签: concurrency parallel-processing synchronization go

我正在构建一个实时系统,看看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)
}

我可以使用类似于我使用CPython执行操作的互斥锁或信号量,但我想利用Go中的频道。

  

**更新

添加WaitGroup会改变程序流程吗?我添加了一个WaitGroup,效果很好。虽然我在整个for循环之后添加了Done()函数,然后整个increment会在decrement之前运行吗?我希望他们尽可能“并行”,我知道只有一个例程可以访问我,但我希望它们彼此独立运行。

2 个答案:

答案 0 :(得分:2)

您的代码存在一些问题:

  1. 两个goroutines都试图同时从频道中读取。这是一个死锁,因为通道中没有任何内容可供阅读。

  2. Println(<-c)从频道中读取一个值,而不是结果。如果您等待两个goroutine完成,它可能会读取结果,但这需要添加WaitGroup。 Waitgroup就像一个信号量,允许每个goroutine减少挂起的goroutine的计数器,并允许调用者等待它们完成某项任务。

  3. 如果没有读者就发送阻止,如果没有发送者则读取被阻止,并且你是一个。等待两个goroutines先完成和b。做一次读取而不是写入(Println读取),你需要一个buffered channel,它在缓冲区中只有一个额外的位置。

  4. 您需要在渠道中提取初始值才能启动流程。

  5. 我已经改变了你的代码,这个例子现在有效(虽然注意它不是真正的增量 - >减少 - >增量 - > ...但是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")
}