golang 协程通道,map,redis的并发性

假如我们没有用协程通道或者加锁的方式,直接并发使用map,会出现线性不安全

例如:

package main

import (
	"time"
	"fmt"
)

var tMap map[int]int

func main() {
	tMap = make(map[int]int)
	for i := 0; i < 10000; i++ {
		go putMap(i)
	}
	time.Sleep(1e10)
	fmt.Println("--------------map = ", len(tMap))
}

func putMap(i int)  {
	tMap[i] = i
}

  

报错:

 

解决方法:

使用锁之后就不会有问题:

package main

import (
	"time"
	"fmt"
	"sync"
)

var tMap map[int]int
var mutex sync.Mutex

func main() {
	tMap = make(map[int]int)
	for i := 0; i < 10000; i++ {
		go putMap(i)
	}
	time.Sleep(1e10)
	fmt.Println("--------------map = ", len(tMap))
}

func putMap(i int)  {
	mutex.Lock()
	tMap[i] = i
	mutex.Unlock()
}

  

又或者是利用协程通道,来保证线程安全

package main

import (
	"time"
	"fmt"
)

var tMap map[int]int

func main() {
	tMap = make(map[int]int)
	data := make(chan int)
	go goroutine(data)
	for i := 0; i < 10000; i++ {
		go putMap(data, i)
	}
	time.Sleep(1e10)
	fmt.Println("--------------map = ", len(tMap))
}

func putMap(data chan int, i int)  {
	data <- i
}

func goroutine(data chan int)  {
	for {
		i := <- data
		tMap[i] = i
	}
}

  

Go的哲学之一就是:不要通过共享内存来通信,而要通过通信来共享内存,前者就是传统的加锁,后者就是Channel。

反正涉及到并发安全性的数据结构,尽量使用协程通道:发送一个数据到Channel 和 从Channel接收一个数据 都是 原子性的。

可以认为加锁的map就是erlang里面的ets,而使用协程通道就是erlang里面的进程里的数据结构

 

最后我们来看一下go对redis的并发操作:

package main

import (
	"time"
	"slg_game_server/server/goredis"
	"slg_game_server/server/util"
)

func main() {
	for i := 0; i < 10000; i++ {
		go putRedis(i)
	}
	time.Sleep(1e10)
}

func putRedis(i int)  {
	goredis.ClientRedis.HSet("hb"+util.ToStr(int32(i)), "hb", "hb"+util.ToStr(int32(i)))
}

  

也没任何问题!!!

因为Redis服务端是个单线程的架构,不同的Client虽然看似可以同时保持连接,但发出去的命令在服务器看来是序列化执行的,

因此对服务端来说,并不存在并发问题!!!

 

posted @ 2018-12-01 15:04  天之草  阅读(769)  评论(0编辑  收藏  举报