1. 程式人生 > 其它 >Go語言的 序列處理 和 並行處理 有什麼區別 ?

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

 

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

商務合作加微信:LetsFeng

goland 全家桶啟用碼,支援所有版本,支援所有系統

連結:http://web.52shizhan.cn/activity/s2abxc

提取碼:GJF9B1DK

現在就開始你的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語言的技術文章或視訊教程,請關注本公眾號獲取並檢視,感謝你的支援與信任!