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条)

报告相同问题?

悬赏问题

  • ¥15 CVRP 图论 物流运输优化
  • ¥15 Tableau online 嵌入ppt失败
  • ¥100 支付宝网页转账系统不识别账号
  • ¥15 基于单片机的靶位控制系统
  • ¥15 真我手机蓝牙传输进度消息被关闭了,怎么打开?(关键词-消息通知)
  • ¥15 下图接收小电路,谁知道原理
  • ¥15 装 pytorch 的时候出了好多问题,遇到这种情况怎么处理?
  • ¥20 IOS游览器某宝手机网页版自动立即购买JavaScript脚本
  • ¥15 手机接入宽带网线,如何释放宽带全部速度
  • ¥30 关于#r语言#的问题:如何对R语言中mfgarch包中构建的garch-midas模型进行样本内长期波动率预测和样本外长期波动率预测