Go語言的 序列處理 和 並行處理 有什麼區別 ?

技术颜良發表於2024-07-02

Go語言的 序列處理 和 並行處理 有什麼區別 ?

Go語言圈
Go語言開發者的學習好助手,分享Go語言知識,技術技巧,學習與交流Go語言開發經驗,互動才有助於技術的提升,每天5分鐘,助你GO語言技術快樂成長
169篇原創內容

最近在學習Golang,所以將學習過程記為筆記,以後翻閱的時候也方便,順便也給大家做一點分享,希望能堅持下去。

學習與交流:Go語言技術交流微信群

現在就開始你的Go語言學習之旅吧!人生苦短,let’s Go.


圖片

圖片

程式運算時往往需要資料,而資料的IO又往往需要時間傳輸,而常見的序列處理,是一個任務處理完成才接著處理新的任務, 其效率低下可想而知。

假如有3個任務要處理,而每個任務都有一定的阻塞,序列處理大概是這個樣子:

main.go

package main

import (
"fmt"
"time"
)

type Task struct {
Duration time.Duration
Name string
}

func main() {
// 宣告要處理的任務
taskList := []Task {
{
1,
"處理1",
},
{
2,
"處理2",
},
{
3,
"處理3",
},
}
starTime := time.Now().Unix()
for _, item := range taskList {
goProcess(item)
}
fmt.Printf("用時:%ds\n", time.Now().Unix() - starTime)
}

/**
* 進行處理
*/
func goProcess(task Task) {
time.Sleep(time.Second * task.Duration) // 假設這是io阻塞會在這裡阻塞一些時間,而這些io可能是網路io也可能是磁碟io等,io完成後才會接著執行下去
fmt.Printf("任務:%s 處理完畢\n", task.Name)
}

處理列印結果
任務:處理1 處理完畢
任務:處理2 處理完畢
任務:處理3 處理完畢
用時:6s

這種序列處理遇到IO阻塞時,弊端就是太費時間了.

可以看到,程式在計算時或許用不到多少時間反而是IO阻塞佔用了大量的時間。這種佔著茅坑不拉屎,外面等著人直跳腳,確實不太好。而用非同步處理則可以跳過阻塞,達到避免佔坑的情況發生。

用協程的話,則可以在阻塞時先非同步執行下去而不用等待,等所有協程都處理結束,再把處理的結果彙總起來就可以了,程式碼大概是這樣子:

main.go

package main

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

type Task struct {
Duration time.Duration
Name string
}

func main() {
// 宣告要處理的任務
taskList := []Task {
{
1,
"處理1",
},
{
2,
"處理2",
},
{
3,
"處理3",
},
}
starTime := time.Now().Unix()
var res []string //處理結果收集
resChang := make(chan string, len(taskList))
wg := &sync.WaitGroup{}
// 這裡收集非同步處理的結果, 透過管道把資料傳遞過來,類似於單一訂閱功能吧
go func() {
wg.Add(1)
defer wg.Done() // 通道關閉後 處理結果也收集完畢,則觸發 用於通知下方批處理,處理結果已經收集完畢
for resItem := range resChang {
res = append(res, resItem)
}
}()
taskWG := &sync.WaitGroup{}
for _, item := range taskList {
taskWG.Add(1) // 批處理 訊號量+1
go goProcess(item, &resChang, taskWG)
}
taskWG.Wait()// 這裡阻塞,等待所有處理執行完畢, 才接著執行下去
close(resChang)// 已經處理完畢後就關閉處理傳輸通道
wg.Wait() // 這是阻塞 等待處理收集完畢, 才接著執行去
// 列印批處理收集的處理結果
for _, i := range res {
fmt.Printf("%s", i)
}
fmt.Printf("用時:%ds\n", time.Now().Unix() - starTime)
}

/**
* 進行處理
*/
func goProcess(task Task, resChan *chan string, taskWG *sync.WaitGroup) {
time.Sleep(time.Second * task.Duration) // 假設這是io阻塞會在這裡阻塞一些時間,而這些io可能是網路io也可能是磁碟io等,才會接著執行下去
res := fmt.Sprintf("任務:%s 處理完畢\n", task.Name)
defer func() {
*resChan <- res // 把處理結果傳出去
taskWG.Done() // 批處理訊號量-1 來報告處理完畢
}()
}

執行結果
任務:處理1 處理完畢
任務:處理2 處理完畢
任務:處理3 處理完畢
用時:3s

相對於之前的序列,這次的並行有效處理IO的阻塞,相當於,序列就是佔坑不用的角,並行則不管這些,你不用,先把你踹開,給有需要的人用先,這樣一腳腳的踹,效率就上一來了。

參考連結:https://www.jb51.net/article/216996.htm

更多相關Go語言的技術文章或影片教程,請關注本公眾號獲取並檢視,感謝你的支援與信任!

學Go語言哪些事兒 · 目錄
上一篇團購買下了beego這個課程,得好好學習了
閱讀 145

相關文章