你是一名大廚,正在煮晚餐。你需要同時煮飯、烤魚、炒菜,而且還要確保每道菜在時間上都剛剛好。這就是 Concurrency ,讓你能像大廚一樣,同時處理多個任務,讓你的程序更高效、更快速。
來了,今天講 Concurrency
!並發
是指 同時執行
多個任務
或 操作
的能力,而 不需要等待一個任務完成才能開始另一個
。在Golang中,可以使用 goroutine
實現 concurrency
,goroutine是一種輕量級的執行緒 Thread
。
Share by communicating
指的是在 Golang
中,通過訊息來 共享數據
,而不是通過共享內存來實現並發操作。這種方式有助於避免多線程中可能出現的競爭條件 Race Condition
等問題。 在Golang中,這種概念通常使用通道(Channels)來實現。
舉例:
package main
import (
"fmt"
"time"
)
func main() {
dataChannel := make(chan int) // 創建一個整數通道
// 創建一個goroutine,將數據發送到通道
go func() {
for i := 1; i <= 5; i++ {
fmt.Printf("客人進來第 %d 位\n", i)
dataChannel <- i // 將數據發送到通道
time.Sleep(time.Second)
}
close(dataChannel) // 關閉通道,表示不再發送數據
}()
// 主函數從通道讀取數據
for num := range dataChannel {
fmt.Printf("接待第 %d 位客人\n", num)
}
fmt.Println("沒位子了,下次請早!")
}
客人進來第 1 位
接待第 1 位客人
客人進來第 2 位
接待第 2 位客人
客人進來第 3 位
接待第 3 位客人
客人進來第 4 位
接待第 4 位客人
客人進來第 5 位
接待第 5 位客人
沒位子了,下次請早!
這種 Share by communicating
的方式確保了數據的 同步訪問
,因為通道在不同goroutine之間傳遞數據時會 自動處理同步問題
,從而減少了競爭條件的風險。
goroutine
是 Go 語言中用於實現並發的概念。每個 goroutine 都是一個 輕量級的執行緒
,可以 獨立運行
,但它們共享相同的地址空間。與傳統操作系統執行緒不同,goroutine 的創建和調度由 Go 的運行時系統自動處理,因此不需要擔心手動管理執行緒。
package main
import (
"fmt"
"time"
)
func sayHello() {
for i := 1; i <= 5; i++ {
fmt.Printf("歡迎光臨!第 %d 位客人\n", i)
time.Sleep(time.Millisecond * 500)
}
}
func main() {
go sayHello() // 啟動一個 goroutine 執行 sayHello 函數
// 主函數休眠一段時間,以確保 goroutine 有足夠的時間執行
time.Sleep(time.Second * 3)
fmt.Println("沒位子了,下次請早!")
}
歡迎光臨!第 1 位客人
歡迎光臨!第 2 位客人
歡迎光臨!第 3 位客人
歡迎光臨!第 4 位客人
歡迎光臨!第 5 位客人
沒位子了,下次請早!
channels
是 Golang 中的一個類型,它像一個 管道
或 隊列
一樣,可以用於 goroutine
之間的 數據傳遞
。通道提供了一種安全的方式,讓一個 goroutine 能夠將數據 發送到通道
,而另一個 goroutine 能夠從通道 接收數據
,這樣可以確保數據在 goroutine 之間的 同步傳遞
。
package main
import "fmt"
func main() {
messageChannel := make(chan string) // 創建一個字串通道
go func() {
messageChannel <- "Hello Channel!" // 發送一個字串到通道
}()
message := <-messageChannel // 從通道接收字串
fmt.Println(message)
close(messageChannel) // 關閉通道
}
Hello Channel!
Channels of channels
是指我們可以使用 通道
作為 另一個通道
的 元素
,從而建立 多層的通道結構
。這種機制可以管理並發程式碼,使通道成為傳遞和同步數據的載體。
package main
import (
"fmt"
"time"
)
func main() {
// 創建一個通道的通道,每個元素都是一個通道,用於傳遞整數
channelOfChannels := make(chan chan int, 3)
// 創建三個 goroutine,每個 goroutine 都有一個獨立的整數通道,並將這些通道傳遞到 channelOfChannels 中
for i := 0; i < 3; i++ {
intChannel := make(chan int)
go func(id int) {
for j := 1; j <= 5; j++ {
intChannel <- j * id
time.Sleep(time.Millisecond * 300)
}
close(intChannel)
}(i + 1)
channelOfChannels <- intChannel
}
// 從 channelOfChannels 中讀取通道,並從這些通道中讀取數據
for i := 0; i < 3; i++ {
intChannel := <-channelOfChannels
for num := range intChannel {
fmt.Printf("從爐火 %d 起鍋上第 %d 道菜\n ", i+1, num)
}
}
close(channelOfChannels)
}
從爐火 1 起鍋上第 1 道菜
從爐火 1 起鍋上第 2 道菜
從爐火 1 起鍋上第 3 道菜
從爐火 1 起鍋上第 4 道菜
從爐火 1 起鍋上第 5 道菜
從爐火 2 起鍋上第 2 道菜
從爐火 2 起鍋上第 4 道菜
從爐火 2 起鍋上第 6 道菜
從爐火 2 起鍋上第 8 道菜
從爐火 2 起鍋上第 10 道菜
從爐火 3 起鍋上第 3 道菜
從爐火 3 起鍋上第 6 道菜
從爐火 3 起鍋上第 9 道菜
從爐火 3 起鍋上第 12 道菜
從爐火 3 起鍋上第 15 道菜
Parallelization
是一種在多核處理器上 同時執行
多個任務
的方式,從而 加快
程序的 執行速度
。在 Golang 中,平行處理是通過 同時運行多個
goroutine 來實現的,每個 goroutine 都可以在不同的核心上執行,並且它們可以通過channels
來進行通信和協調。
package main
import (
"fmt"
"sync"
)
func calculateSum(start, end int, wg *sync.WaitGroup, resultChan chan int) {
defer wg.Done()
sum := 0
for i := start; i <= end; i++ {
sum += i
}
resultChan <- sum
}
func main() {
numTasks := 4
resultChan := make(chan int, numTasks)
var wg sync.WaitGroup
for i := 0; i < numTasks; i++ {
wg.Add(1)
go calculateSum(i*25+1, (i+1)*25, &wg, resultChan)
}
go func() {
wg.Wait()
close(resultChan)
}()
totalSum := 0
for partialSum := range resultChan {
totalSum += partialSum
}
fmt.Printf("總和:%d\n", totalSum)
}
總和:5050
A leaky buffer
是一種用於在生產者和消費者之間進行數據交換的機制。然而,如果緩衝區已滿,新的數據將仍然被接受,但這將導致緩衝區中的 舊數據被丟失
。這種緩衝區設計通常是不安全的,因為它可能導致內存泄漏或數據丟失,特別是在 高負載
情況下。
假設有一間餐廳只有5個座位。現在,有5個客人想要進來用餐但沒有預訂。當第6個客人來時將他安排入座,但這樣會導致一個問題「餐廳只有5個座位,現在需要讓一個現有的客人離開,否則無法容納所有的客人。」這導致會有一個客人被拒絕服務或者將一個客人趕出餐廳。
餐廳的座位數就好比 緩衝區的大小
,客人就好比要存放在緩衝區中的 數據
。如果 緩衝區已滿
,而新的數據仍然被接受,這可能導致一些數據被 覆蓋
或 丟失
,就像在餐廳中有可能讓一個現有的客人被拒絕服務一樣。這樣會導致數據錯誤或程序崩潰。
package main
import (
"fmt"
"time"
)
func main() {
bufferSize := 2
dataChannel := make(chan int, bufferSize)
go func() {
for i := 1; i <= 5; i++ {
dataChannel <- i // 將數據發送到有漏洞的緩衝區
fmt.Printf("點餐%d\n", i)
time.Sleep(time.Millisecond * 500)
}
close(dataChannel)
}()
for num := range dataChannel {
fmt.Printf("開始煮%d\n", num)
time.Sleep(time.Millisecond * 1000)
}
}
點餐1
開始煮1
點餐2
開始煮2
點餐3
點餐4
開始煮3
點餐5
開始煮4
開始煮5
上班同時打game、傳line、達標OKR,concurrency人,你是自己的超人。