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

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

例如:安全

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。spa

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

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

 

最后咱们来看一下go对redis的并发操做:server

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虽然看似能够同时保持链接,但发出去的命令在服务器看来是序列化执行的,

所以对服务端来讲,并不存在并发问题!!!

相关文章
相关标签/搜索