dragon456101 2018-07-08 18:32
浏览 32
已采纳

缓冲通道旋转,简单锁定

The following is loosely based on Go in Practice (page 81):

$ cat simple_locking_with_buffered_channels.go 
package main

import(
    "fmt"
    "time"
)

func main(){
    reap := 0; sow := 0
    lock := make(chan bool,4100)
    for i:=0; i<4001; i++{
        go worker(i, lock, &reap)
        sow += 1
    }
    for reap != sow {
        fmt.Println("*yawn*")
        time.Sleep(100 * time.Millisecond)
    }
    close(lock)
}


func worker(i int, lock chan bool, reap *int){
    fmt.Printf("%d wants the lock
", i)
    lock <-true // we acquire the lock thusly.
    fmt.Printf("%d has the lock
", i)
    time.Sleep(500 * time.Millisecond) 
    fmt.Printf("%d is releasing the lock
", i)
    *reap += 1
    <-lock // release
}

When I run it, most of the time it finishes, but occasionally I see that it spins on yawn - perpetually so, until it is killed. Yes, I can add a timeout logic, but I want to know why is this happening.

$ ps -p `pgrep  simple_locking` -o lstart,etime
                 STARTED     ELAPSED
Sun Jul  8 11:34:59 2018       02:41
$ ps -p `pgrep  simple_locking` -o lstart,etime
                 STARTED     ELAPSED
Sun Jul  8 11:34:59 2018       03:24

It is supposed to work, then why is the odd behavior happening. in those cases, why is my reap != sow ?

~/golearn $ go version
go version go1.10.3 linux/amd64

I am running this on an busy old-ish linux laptop, I am baffled why does it spin intermittently? Thanks in advance!

https://play.golang.org/p/BJwAmRf1OXB

update : 1

I changed the code to use mutex (or so I think..) as:

package main

import(
    "fmt"
    "time"
    "sync"
)

var mutex sync.Mutex

func main(){
    reap := 0; sow := 0
    lock := make(chan bool,400)
    for i:=0; i<389; i++{
        go worker(i, lock, &reap)
        sow += 1
    }
    time.Sleep(100 * time.Millisecond)
    for reap != sow {
        fmt.Println("*yawn*")
        time.Sleep(100 * time.Millisecond)
    }
    close(lock)
}


func worker(i int, lock chan bool, reap *int){
    fmt.Printf("%d wants the lock
", i)
    lock <-true // we acquire the lock thusly.
    fmt.Printf("%d has the lock
", i)
    time.Sleep(500 * time.Millisecond) 
    fmt.Printf("%d is releasing the lock
", i)
    mutex.Lock()
    *reap += 1
    mutex.Unlock()
    <-lock // release
}

Is this the right way, since go run --race still says WARNING: DATA RACE ?

*update 3: *

After trying go's atomic counters, which require delays between increments, I ended up using mutex. What I learned was : even reading (as opposed to writing) can make it complain of race conditions. So here, I wrapped my call in a function call which uses mutex to read, and it clears the --race tests:

$ cat improv.go
package main

import(
        "fmt"
        "time"
        "sync"
)

var mutex sync.Mutex

func main(){
        sow := 0
        reap := 0

        lock := make(chan bool,40)
        for i:=0; i<38; i++{
                go worker(i, lock, &reap)
                sow += 1
        }
        time.Sleep(100 * time.Millisecond)


        //for  get_counter(&reap) != get_counter(&sow) {
        for  get_counter(&reap) != sow {
                fmt.Println("*yawn*")
                time.Sleep(100 * time.Millisecond)
        }
}

func worker(i int, lock chan bool, reap *int){
        fmt.Printf("%d wants the lock
", i)
        lock <-true // we acquire the lock thusly.
        fmt.Printf("%d has the lock
", i)
        time.Sleep(500 * time.Millisecond)
        fmt.Printf("%d is releasing the lock
", i)
        mutex.Lock()
        defer mutex.Unlock()
        *reap += 1
        <-lock // release
}

func get_counter(counter *int) int {
        mutex.Lock()
        defer mutex.Unlock()
        return *counter
}

 $ go run --race improv.go >/dev/null
  • 写回答

3条回答 默认 最新

  • duano3557 2018-07-09 20:35
    关注

    thanks for the tutelage, I learned that merely reading a variable that's being written elsewhere into, can make --race complain of WARNING: DATA RACE. So here, I wrapped my call in a function call which uses mutex to read, and it clears the --race test.

    package main
    
    import (
        "fmt"
        "sync"
        "time"
    )
    
    var mutex sync.Mutex
    
    func main() {
        sow := 0
        reap := 0
    
        lock := make(chan bool, 40)
        for i := 0; i < 38; i++ {
            go worker(i, lock, &reap)
            sow += 1
        }
        time.Sleep(100 * time.Millisecond)
    
        //for  get_counter(&reap) != get_counter(&sow) {
        for get_counter(&reap, &sow) {
            fmt.Println("*yawn*")
            time.Sleep(100 * time.Millisecond)
        }
    }
    
    func worker(i int, lock chan bool, reap *int) {
        fmt.Printf("%d wants the lock
    ", i)
        lock <- true
        fmt.Printf("%d has the lock
    ", i)
        time.Sleep(500 * time.Millisecond)
        fmt.Printf("%d is releasing the lock
    ", i)
        mutex.Lock()
        defer mutex.Unlock()
        *reap += 1
        <-lock
    }
    
    func get_counter(reap *int, sow *int) bool {
        mutex.Lock()
        defer mutex.Unlock()
        return *reap == *sow
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

悬赏问题

  • ¥20 access多表提取相同字段数据并合并
  • ¥20 基于MSP430f5529的MPU6050驱动,求出欧拉角
  • ¥20 Java-Oj-桌布的计算
  • ¥15 powerbuilder中的datawindow数据整合到新的DataWindow
  • ¥20 有人知道这种图怎么画吗?
  • ¥15 pyqt6如何引用qrc文件加载里面的的资源
  • ¥15 安卓JNI项目使用lua上的问题
  • ¥20 RL+GNN解决人员排班问题时梯度消失
  • ¥60 要数控稳压电源测试数据
  • ¥15 能帮我写下这个编程吗