douzhanyan5015 2017-01-11 03:27
浏览 35
已采纳

从带锁的地图读取不会通过通道返回值

I tried to implement a locking version of reading/writing from a map in golang, but it doesn't return the desired result.

package main

import (
    "sync"
    "fmt"
)

var m = map[int]string{}
var lock = sync.RWMutex{}

func StoreUrl(id int, url string) {
        for {
                lock.Lock()
                defer lock.Unlock()

                m[id] = url
        }
}

func LoadUrl(id int, ch chan string) {
    for {
        lock.RLock()
        defer lock.RUnlock()

        r := m[id]
        ch <- r
    }
}

func main() {
    go StoreUrl(125, "www.google.com")

    chb := make(chan string)
    go LoadUrl(125, chb);

    C := <-chb
    fmt.Println("Result:", C)                           
}

The output is:

Result: 

Meaning the value is not returned via the channel, which I don't get. Without the locking/goroutines it seems to work fine. What did I do wrong?

The code can also be found here:

https://play.golang.org/p/-WmRcMty5B

  • 写回答

2条回答 默认 最新

  • dongliling6336 2017-01-11 04:31
    关注

    Infinite loops without sleep or some kind of IO are always bad idea.

    In your code if you put a print statement at the start of StoreUrl, you will find that it never gets printed i.e the go routine was never started, the go call is setting putting the info about this new go routine in some run queue of the go scheduler but the scheduler hasn't ran yet to schedule that task. How do you run the scheduler? Do sleep/IO/channel reading/writing.

    Another problem is that your infinite loop is taking lock and trying to take the lock again, which will cause it to deadlock. Defer only run after function exit and that function will never exit because of infinite loop.

    Below is modified code that uses sleep to make sure every execution thread gets time to do its job.

    package main
    
    import (
        "sync"
        "fmt"
        "time"
    )
    
    var m = map[int]string{}
    var lock = sync.RWMutex{}
    
    func StoreUrl(id int, url string) {
            for {
                    lock.Lock()
                    m[id] = url
                    lock.Unlock()
                    time.Sleep(1)
            }
    }
    
    func LoadUrl(id int, ch chan string) {
        for {
                lock.RLock()
                r := m[id]
                lock.RUnlock()
                ch <- r
    
        }
    }
    
    func main() {
        go StoreUrl(125, "www.google.com")
        time.Sleep(1)
        chb := make(chan string)
        go LoadUrl(125, chb);
    
        C := <-chb
        fmt.Println("Result:", C)
    }
    

    Edit: As @Jaun mentioned in the comment, you can also use runtime.Gosched() instead of sleep.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥100 任意维数的K均值聚类
  • ¥15 stamps做sbas-insar,时序沉降图怎么画
  • ¥15 unity第一人称射击小游戏,有demo,在原脚本的基础上进行修改以达到要求
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看
  • ¥15 关于#Java#的问题,如何解决?
  • ¥15 加热介质是液体,换热器壳侧导热系数和总的导热系数怎么算
  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
  • ¥15 cmd cl 0x000007b
  • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line
  • ¥500 火焰左右视图、视差(基于双目相机)