golang线程安全映射,带有通道作为线程安全的值

时间:2015-05-20 02:01:26

标签: dictionary concurrency go

我使用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)
}

1 个答案:

答案 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线上检索了相同的通道。

有意义吗?