GO語言不要用共享內存來通信,要用通信來共享內存


這句話是推薦使用channel來實現 "讓同一塊內存在同一時間內只被一個線程操作" 的目的

 

先看一個簡單的示例代碼

package main

import (
	"fmt"
	"net/http"
)

var j int

func HelloServer(w http.ResponseWriter, req *http.Request) {
	j++
	fmt.Println(j)
}

func main() {
	http.HandleFunc("/", HelloServer)
	http.ListenAndServe(":8080", nil)
}

每個http請求都會創建一個新的goroutine,在高並發下,多個goroutine對全局變量 j 進行同時讀寫。有可能造成這么一個局面:

前面一個goroutine在 j++ 之后,此時 j 值為5,本來應該輸出5的,結果輸出之前另一個goroutine又執行了一次 j++ ,這樣一來 j 值就為6,然后兩個goroutine一起輸出了6

 

下面兩個方法可以解決該問題:

1. 前者 - 【用共享內存來通信】,就是上鎖。將例子中代碼修改如下

package main

import (
	"sync"
	"fmt"
	"net/http"
)

var j int
var m sync.Mutex    // 互斥鎖

func HelloServer(w http.ResponseWriter, req *http.Request) {
	m.Lock()
	j++
	fmt.Println(j)
	m.Unlock()
}

func main() {
	http.HandleFunc("/", HelloServer)
	http.ListenAndServe(":8080", nil)
}

將用戶要進行業務流程之前上鎖,執行完后解鎖。這樣每次同一時間就只有一個goroutine在操作內存變量了

 

2. 后者 - 【要用通信來共享內存】,就是使用goroutine + channel,將例子中代碼修改如下

package main

import (
	"fmt"
	"net/http"
)

var j int
var chGoto = make(chan int)

func HelloServer(w http.ResponseWriter, req *http.Request) {
	// 將干活信號傳給goroutine
	chGoto <- 1
}

func rec() {
	for {
		// 等待干活信號
		<- chGoto
		// 開始干活
		j++
		fmt.Println(j)
	}
}

func main() {
	// 先創建一個goroutine
	go rec()
	http.HandleFunc("/", HelloServer)
	http.ListenAndServe(":8080", nil)
}

主協程專門創建一個goroutine用來操作內存,創建完畢后rec()堵塞,等待其他goroutine往chGoto傳值,假設http請求A往chGoto傳值,當rec()執行完A傳入的 <- chGoto 之后,另一個http請求B緊接着又會向chGoto傳值,但此時rec()還在執行從A傳值chGoto的for循環里還沒執行的語句,執行完后rec()再從chGoto中取出B的傳值。這樣一來高並發下多個goroutine只有依靠單個rec()能操作內存,達到了 "讓同一塊內存在同一時間內只被一個線程操作" 的目的


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM