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

时间:2017-01-11 03:27:41

标签: go concurrency

我试图在golang中从地图实现读/写的锁定版本,但它并没有返回所需的结果。

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

输出结果为:

Result: 

意味着价值不是通过渠道返回的,我没有得到。没有锁定/ goroutines它似乎工作正常。我做错了什么?

代码也可以在这里找到:

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

2 个答案:

答案 0 :(得分:1)

没有睡眠或某种IO的无限循环总是坏主意。

在您的代码中,如果您在StoreUrl的开头放置一个print语句,您会发现它永远不会被打印,即go例程从未启动过,go调用是设置有关这个新的信息go调度程序的某些运行队列中的例程,但调度程序尚未运行以安排该任务。你如何运行调度程序?睡眠/ IO /频道读/写。

另一个问题是你的无限循环正在锁定并试图再次锁定,这将导致它死锁。延迟仅在函数退出后运行,并且由于无限循环,该函数将永远不会退出。

下面是修改后的代码,它使用sleep来确保每个执行线程都有时间完成它的工作。

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

修改:@Jaun在评论中提到,您也可以使用runtime.Gosched()代替睡眠。

答案 1 :(得分:1)

延迟不正确的使用,在函数结束时延迟执行,而不是语句。

development:
  encoding: unicode
  username: postgres
  password: postgres
  pool: 20
  timeout: 5000
  wait_timeout: 10
  min_messages: warning
  adapter: postgresql
  host: localhost
  database: auction_dev
  port: 5432

func StoreUrl(id int, url string) {
    for {
        func() {
            lock.Lock()
            defer lock.Unlock()
            m[id] = url
        }()
    }
}

我们无法控制go例程的顺序,所以添加time.Sleep()来控制命令。

代码在这里:

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