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

huangapple go评论76阅读模式
英文:

Reading from map with locks doesn't return value via channel

问题

我尝试在Go语言中实现了一个带锁的读写map的版本,但是它没有返回期望的结果。

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: 

这意味着值没有通过通道返回,我不明白为什么。在没有锁和goroutine的情况下,它似乎工作正常。我做错了什么?

你也可以在这里找到代码:

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

英文:

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 (
	&quot;sync&quot;
	&quot;fmt&quot;
)

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 &lt;- r
	}
}

func main() {
	go StoreUrl(125, &quot;www.google.com&quot;)

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

	C := &lt;-chb
	fmt.Println(&quot;Result:&quot;, 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

答案1

得分: 1

无限循环而没有休眠或某种IO操作总是一个坏主意。

在你的代码中,如果你在StoreUrl的开头放置一个打印语句,你会发现它从未被打印出来,也就是说go例程从未启动,go调用将关于这个新go例程的信息放入go调度器的某个运行队列中,但调度器尚未运行以安排该任务。你如何运行调度器?使用sleep/IO/channel读写。

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

下面是修改后的代码,使用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()代替sleep。

英文:

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 (
	&quot;sync&quot;
	&quot;fmt&quot;
	&quot;time&quot;
)

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 &lt;- r
		
	}
}

func main() {
	go StoreUrl(125, &quot;www.google.com&quot;)
    time.Sleep(1)
    chb := make(chan string)
	go LoadUrl(125, chb);

	C := &lt;-chb
	fmt.Println(&quot;Result:&quot;, C)
}

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

答案2

得分: 1

使用defer的方式不正确,defer语句会在函数结束时执行,而不是在语句执行结束时执行。

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

由于无法控制goroutine的顺序,因此可以添加time.Sleep()来控制顺序。

代码链接:

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

英文:

Usage of defer incorrect, defer execute at end of function, not for statement.

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

or

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

We can't control the order of go routine, so add time.Sleep() to control the order.

code here:

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

huangapple
  • 本文由 发表于 2017年1月11日 11:27:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/41582210.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定