我使用this作为concurrentmap,缓冲通道作为线程安全的地图值(作为队列),当测试使用10个goroutines时,从通道获得的值与发送的值不同,是否有任何建议?
class ViewController: UIViewController {
var allowTurning = false
override func supportedInterfaceOrientations() -> Int {
if allowTurning {
return Int(UIInterfaceOrientationMask.AllButUpsideDown.rawValue)
} else {
return Int(UIInterfaceOrientationMask.Portrait.rawValue)
}
}
@IBAction func doTurn(sender: UIButton) {
allowTurning = true
let fakeViewController = UIViewController()
presentViewController(fakeViewController, animated: false) {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.dismissViewControllerAnimated(false, completion: {
NSLog("Done dismissing!")
})
})
}
}
}
更新我误解了业务逻辑,代码应该是这样的
package main
import "fmt"
import "github.com/streamrail/concurrent-map"
func main() {
testmap := cmap.New()
fmt.Println("SyncMapNew: ", TestInParallel(&testmap, 10))
}
func TestInParallel(g *cmap.ConcurrentMap, n int) time.Duration {
start := time.Now()
var wait sync.WaitGroup
for i := 0; i < n; i++ {
wait.Add(1)
go func() {
TheTest(g, rand.New(rand.NewSource(int64(i*500))))
wait.Done()
}()
}
wait.Wait()
return time.Now().Sub(start)
}
func TheTest(g *cmap.ConcurrentMap, rnd *rand.Rand) time.Duration {
start := time.Now()
var key string
var value time.Time
for i := 0; i < 10000; i++ {
key = strconv.Itoa(int(rnd.Int31n(50000)))
if g.Has(key) == false {
g.Set(key, make(chan time.Time, 100))
}
tchan, _ := g.Get(key)
castchan := tchan.(chan time.Time)
value = time.Now()
castchan <- value
got := <-castchan
g.Set(key, castchan)
if value != got {
panic(fmt.Sprintf("ERROR: expected %v, got %v", value, got))
}
}
return time.Now().Sub(start)
}
答案 0 :(得分:0)
您使用的是随机密钥,因此多个goroutine可能会获得相同的随机数。
如果两个例程在相同(ish)时间获得相同的数字,那么
这是竞争条件:
if g.Has(key) == false {
g.Set(key, make(chan time.Time, 100))
}
有可能h.Has在同一时间为两个goroutine是假的,然后它们都设置,所以goroutine1设置chan A,goroutine2设置chan B然后两个都使用chan B
为了解决这个问题,你需要像
这样的东西SetIfAbsent
哪个锁定,检查它是否存在,如果不存在,则设置它,然后解锁。
您链接的图谱不是超级有用的缓存,因为它不提供原子SetIfAbsent类型函数。
如果没有g.Has / g.Set竞赛,那么如果两个例程碰巧得到相同的密钥,因此相同的频道,则无法保证哪个值是队列中的第一个首先阅读。
所以goroutine1可能会读取goroutine2放入的值,或者相反。
当考虑在并发执行的系统中使用共享状态时,您必须假设在任何语句/代码行之间可能发生任何其他操作。
我经常想到它,你应该假设你的代码的每一行都是一次在每个核心上运行。
所以,在Has / Set示例中,它将是:
if g.Has(key) == false { // goroutine1
if g.Has(key) == false { // goroutine2
g.Set(key, make(chan time.Time, 100)) //goroutine1
g.Set(key, make(chan time.Time, 100)) //goroutine2
} //goroutine1
} //goroutine2
tchan, _ := g.Get(key) //goroutine1
tchan, _ := g.Get(key) //goroutine2
查看错误的位置?第二个例程将它的通道放在地图中,但两者都在tchan线上检索了相同的通道。
有意义吗?