我试图在golang中实现从地图读取/写入的锁定版本,但它没有返回所需的结果。
包主
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:
这意味着该值不会通过通道返回,但我没有得到。没有锁定/执行例程,它似乎可以正常工作。我做错了什么?
代码也可以在这里找到:
https://play.golang.org/p/-WmRcMty5B
没有睡眠或某种 IO 的无限循环总是坏主意。
在你的代码中,如果你把一个 print 语句放在 StoreUrl
的开头,你会发现它永远不会被打印,即 go 例程从未启动,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()
而不是睡眠。
defer 的用法不正确,在函数结束时延迟执行,而不是 for 语句。
func StoreUrl(id int, url string) {
for {
func() {
lock.Lock()
defer lock.Unlock()
m[id] = url
}()
}
}
或
func StoreUrl(id int, url string) {
for {
lock.Lock()
m[id] = url
lock.Unlock()
}
}
我们无法控制 go 例程的顺序,因此请添加时间。Sleep() 来控制顺序。
代码在这里:
https://play.golang.org/p/Bu8Lo46SA2