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雖然看似能夠同時保持鏈接,但發出去的命令在服務器看來是序列化執行的,

所以對服務端來講,並不存在併發問題!!!

相關文章
相關標籤/搜索