測試一下Go語言goroutine的魅力

本段測試代碼的說明

這里用go語言簡單的寫了一個(gè)創(chuàng)建訂單的業(yè)務(wù)流程,目的是測試一下Go語言的goroutine的魅力,注意,只是測試目的,代碼并不存在任何實(shí)用性.

測試邏輯說明

創(chuàng)建訂單的業(yè)務(wù)邏輯,一般情況下都需要下面的幾個(gè)步驟:
1.檢查訂單商品ID是否有效
2.檢查訂單商品ID的數(shù)量是否足夠本次創(chuàng)建訂單
3.檢查優(yōu)惠券是否可用
4.計(jì)算每個(gè)商品的訂單價(jià)格(這里可能是查詢商品的最新單價(jià)*數(shù)量)
5.計(jì)算訂單金額
6.創(chuàng)建訂單

測試代碼中,已經(jīng)把上面的6個(gè)步驟,全部封裝成了獨(dú)立的函數(shù),每個(gè)函數(shù)內(nèi)都啟用了延時(shí)1秒的操作來模擬真實(shí)
情況下的一些時(shí)間的耗費(fèi);

最終目的,是想要分別嘗試一下,用goroutine方式和不用goroutine方式的區(qū)別,和性能差異!

測試結(jié)果

這里是測試代碼的兩次運(yùn)行結(jié)果,從結(jié)果中可以看到,首先是程序的執(zhí)行過程的不同,再就是程序耗費(fèi)時(shí)間的不同,啟用goroutine的程序耗費(fèi)時(shí)間是3.0122762s,而沒啟用goroutine的程序耗費(fèi)時(shí)間是9.003462s,這性能差距兼職令人驚喜啊

D:\go\src\github.com\just5325\day01\04_channel>go run main.go
請(qǐng)選擇是否啟用子協(xié)程處理任務(wù)(1:使用 0:不使用 直接回車默認(rèn)為1):
1
正在計(jì)算訂單金額
正在檢查商品是否有效 ID: 1
正在計(jì)算每個(gè)商品的訂單價(jià)格 ID: 1
正在檢查商品數(shù)量是否有效 ID:1 數(shù)量:1
正在檢查優(yōu)惠券是否可用 ID:1
正在計(jì)算每個(gè)商品的訂單價(jià)格 ID: 2
正在檢查商品是否有效 ID: 2
正在檢查商品數(shù)量是否有效 ID:2 數(shù)量:1
正在創(chuàng)建訂單,訂單金額: 100
結(jié)束! [已啟用子協(xié)程],程序耗費(fèi)時(shí)間:3.0122762s

D:\go\src\github.com\just5325\day01\04_channel>go run main.go
請(qǐng)選擇是否啟用子協(xié)程處理任務(wù)(1:使用 0:不使用 直接回車默認(rèn)為1):
0
正在檢查商品是否有效 ID: 1
正在檢查商品是否有效 ID: 2
正在檢查商品數(shù)量是否有效 ID:1 數(shù)量:1
正在檢查商品數(shù)量是否有效 ID:2 數(shù)量:1
正在檢查優(yōu)惠券是否可用 ID:1
正在計(jì)算每個(gè)商品的訂單價(jià)格 ID: 1
正在計(jì)算每個(gè)商品的訂單價(jià)格 ID: 2
正在計(jì)算訂單金額
正在創(chuàng)建訂單,訂單金額: 100
結(jié)束! [未啟用子協(xié)程],程序耗費(fèi)時(shí)間:9.003462s

測試代碼

package main

import (
    "fmt"
    "time"
)

// 是否啟用子子協(xié)程 false:不啟用(就像我們寫PHP代碼一樣從上至下的順序運(yùn)行代碼) true:啟用(啟用多個(gè)協(xié)程同時(shí)處理不同的業(yè)務(wù)邏輯,相關(guān)聯(lián)的數(shù)據(jù)通過通道傳輸)
var subGoroutine bool = false

/**
@title 檢查商品是否有效
@params productData []map[string]int 商品數(shù)據(jù)
@params out chan<- bool 發(fā)送數(shù)據(jù)的通道,通道內(nèi)傳遞的值為是否有效 true:有效 false:無效
*/
func checkProductById(productData []map[string]int, out chan bool) {
    checkProductId := make(chan int, 10)
    funcRet := make(chan bool, 10)
    // 開啟子協(xié)程,把商品ID逐個(gè)發(fā)送到通道中
    go func() {
        for _, value := range productData {
            checkProductId <- value["id"]
        }
        // 關(guān)閉通道
        close(checkProductId)
    }()

    // 開啟子協(xié)程,遍歷商品ID,檢查通道中的每個(gè)商品是否有效
    go func() {
        // for range遍歷通道,值為商品ID,通道關(guān)閉后會(huì)自動(dòng)結(jié)束循環(huán),所以這里只管遍歷接收通道的數(shù)據(jù)并處理后發(fā)送數(shù)據(jù)到通道就OK了
        for value := range checkProductId {
            // 延時(shí)1秒,代表這里執(zhí)行的各種耗費(fèi)時(shí)間的任務(wù)
            fmt.Println("正在檢查商品是否有效 ID:", value)
            time.Sleep(time.Second * 1)

            // 我們就當(dāng)所有商品ID檢查結(jié)果都為有效
            funcRet <- true
        }
        close(funcRet)
    }()

    // 遍歷通道,發(fā)送數(shù)據(jù)
    for v := range funcRet {
        out <- v
    }
    close(out)
}

/*
@title 檢查商品數(shù)量是否有效
@params productData []map[string]int 商品數(shù)據(jù)
@params out chan<- bool 發(fā)送數(shù)據(jù)的通道,通道內(nèi)傳遞的值為是否有效 true:有效 false:無效
*/
func checkProductNumById(productData []map[string]int, out chan bool) {
    checkProductNum := make(chan map[string]int, 10)
    funcRet := make(chan bool, 10)
    // 開啟子協(xié)程,把商品ID逐個(gè)發(fā)送到通道中
    go func() {
        for _, value := range productData {
            checkProductNum <- value
        }
        // 關(guān)閉通道
        close(checkProductNum)
    }()

    // 開啟子協(xié)程,遍歷商品信息,檢查通道中的每個(gè)商品的數(shù)量是否有效
    go func() {
        // for range遍歷通道,值為商品信息,通道關(guān)閉后會(huì)自動(dòng)結(jié)束循環(huán),所以這里只管遍歷接收通道的數(shù)據(jù)并處理后發(fā)送數(shù)據(jù)到通道就OK了
        for value := range checkProductNum {
            // 延時(shí)1秒,代表這里執(zhí)行的各種耗費(fèi)時(shí)間的任務(wù)
            fmt.Printf("正在檢查商品數(shù)量是否有效 ID:%d 數(shù)量:%d\n", value["id"], value["num"])
            time.Sleep(time.Second * 1)

            // 我們就當(dāng)所有商品ID檢查結(jié)果都為有效
            funcRet <- true
        }
        close(funcRet)
    }()

    // 遍歷通道,發(fā)送數(shù)據(jù)
    for v := range funcRet {
        out <- v
    }
    close(out)
}

/*
@title 檢查優(yōu)惠券是否可用
@params couponId int 優(yōu)惠券ID
@params out chan<- bool 發(fā)送數(shù)據(jù)的通道,通道內(nèi)傳遞的值為是否有效 true:有效 false:無效
*/
func checkCoupon(couponId int, out chan int) {
    // 延時(shí)1秒,代表這里執(zhí)行的各種耗費(fèi)時(shí)間的任務(wù)
    fmt.Printf("正在檢查優(yōu)惠券是否可用 ID:%d\n", couponId)
    time.Sleep(time.Second * 1)

    // 發(fā)送數(shù)據(jù)到通道
    out <- 100
    close(out)
}

/**
@title 計(jì)算每個(gè)商品的訂單價(jià)格(這里可能是查詢商品的最新單價(jià)*數(shù)量)
@params productData []map[string]int 商品數(shù)據(jù)
@params out chan<- int 發(fā)送數(shù)據(jù)的通道,通道內(nèi)傳遞的值為是訂單中商品的金額
*/
func getOrderProductAmountById(productData []map[string]int, out chan int) {
    checkProductId := make(chan int, 10)
    funcRet := make(chan int, 10)
    // 開啟子協(xié)程,把商品ID逐個(gè)發(fā)送到通道中
    go func() {
        for _, value := range productData {
            checkProductId <- value["id"]
        }
        // 關(guān)閉通道
        close(checkProductId)
    }()

    // 開啟子協(xié)程,遍歷商品ID,檢查通道中的每個(gè)商品是否有效
    go func() {
        // for range遍歷通道,值為商品ID,通道關(guān)閉后會(huì)自動(dòng)結(jié)束循環(huán),所以這里只管遍歷接收通道的數(shù)據(jù)并處理后發(fā)送數(shù)據(jù)到通道就OK了
        for value := range checkProductId {
            // 延時(shí)1秒,代表這里執(zhí)行的各種耗費(fèi)時(shí)間的任務(wù)
            fmt.Println("正在計(jì)算每個(gè)商品的訂單價(jià)格 ID:", value)
            time.Sleep(time.Second * 1)

            // 我們就當(dāng)所有商品ID檢查結(jié)果都為有效
            funcRet <- 100
        }
        close(funcRet)
    }()

    // 遍歷通道,發(fā)送數(shù)據(jù)
    for v := range funcRet {
        out <- v
    }
    close(out)
}

/**
@title 計(jì)算訂單金額
@params productAmount chan int 商品金額
@params couponAmount chan int 優(yōu)惠券金額
@params out chan<- int 發(fā)送數(shù)據(jù)的通道,通道內(nèi)傳遞的值為訂單金額
*/
func getOrderAmount(productAmount chan int, couponAmount chan int, out chan int) {
    // 延時(shí)1秒,代表這里執(zhí)行的各種耗費(fèi)時(shí)間的任務(wù)
    fmt.Println("正在計(jì)算訂單金額")
    time.Sleep(time.Second * 1)

    // 訂單總價(jià)
    var orderAmount int
    for v := range productAmount {
        orderAmount += v
    }
    // 不考慮滿減,能用的優(yōu)惠券就減
    for v := range couponAmount {
        orderAmount -= v
    }
    // 發(fā)送數(shù)據(jù)到通道
    out <- orderAmount
    close(out)
}

/**
@title 創(chuàng)建訂單
@params orderAmountChannel chan int 訂單金額
*/
func create(orderAmountChannel chan int) {
    // 訂單總價(jià)
    var orderAmount int
    for v := range orderAmountChannel {
        orderAmount = v
    }

    // 延時(shí)1秒,代表這里執(zhí)行的各種耗費(fèi)時(shí)間的任務(wù)
    fmt.Println("正在創(chuàng)建訂單,訂單金額:", orderAmount)
    time.Sleep(time.Second * 1)
}

/**
@title 獲取用戶輸入信息,配置是否使用子協(xié)程
@params isSubGoroutine int 請(qǐng)選擇是否啟用子協(xié)程處理任務(wù)(1:使用 0:不使用 直接回車默認(rèn)為1)
*/
func inputIsSubGoroutine() bool {
    // 聲明一個(gè)int類型的變量,接收用戶輸入的信息
    var isSubGoroutine int
    // 打印輸入提示信息
    fmt.Println("請(qǐng)選擇是否啟用子協(xié)程處理任務(wù)(1:使用 0:不使用 直接回車默認(rèn)為1):")
    // 預(yù)先聲明一個(gè)error類型的變量,用作接收錯(cuò)誤信息
    var err error
    // fmt.Scanln從標(biāo)準(zhǔn)輸入os.Stdin讀取文本,會(huì)在讀取到換行時(shí)停止.
    _, err = fmt.Scanln(&isSubGoroutine)
    // 使用if語句,初始化定義一個(gè)變量為空,如果有錯(cuò)誤信息,提示錯(cuò)誤并退出程序
    if errorMsg := ""; err != nil {
        // 這里我們正好也練習(xí)一下switch的寫法,實(shí)際上和php也是一樣的
        switch err.Error() {
        case "unexpected newline":
            // 未輸入?yún)?shù)時(shí),不報(bào)錯(cuò),默認(rèn)為1,使用子協(xié)程
            isSubGoroutine = 1
        case "expected newline", "expected integer":
            errorMsg = "參數(shù)錯(cuò)誤,只允許輸入整型數(shù)字的 0 或者 1 "
        default:
            errorMsg = fmt.Sprintf("參數(shù)錯(cuò)誤,Error: [%s]", err.Error())
        }
        fmt.Println(errorMsg)
    }
    if isSubGoroutine == 1 {
        subGoroutine = true
    } else {
        subGoroutine = false
    }
    return subGoroutine
}

func main() {
    // 獲取用戶輸入信息,配置是否使用子協(xié)程
    inputIsSubGoroutine()

    // 程序開始運(yùn)行時(shí)間
    startDate := time.Now()
    // 練習(xí)一下channel通道的使用,簡單的寫一下創(chuàng)建訂單的業(yè)務(wù)流程吧

    // 聲明一個(gè)變量productData,類型為切片,值為map類型,存儲(chǔ)創(chuàng)建訂單的商品信息
    var productData []map[string]int
    productData = append(productData, map[string]int{
        // id的表示商品id
        "id": 1,
        // num的表示購買數(shù)量
        "num": 1,
    })
    productData = append(productData, map[string]int{
        "id":  2,
        "num": 1,
    })

    // 聲明一個(gè)變量couponId,表示訂單所使用的優(yōu)惠券ID
    couponId := 1
    // OK,到這里進(jìn)入主業(yè)務(wù)流程了
    // 一般情況下都需要的幾個(gè)步驟:
    // 1.檢查訂單商品ID是否有效
    // 2.檢查訂單商品ID的數(shù)量是否足夠本次創(chuàng)建訂單
    // 3.檢查優(yōu)惠券是否可用
    // 4.計(jì)算每個(gè)商品的訂單價(jià)格(這里可能是查詢商品的最新單價(jià)*數(shù)量)
    // 5.計(jì)算訂單金額
    // 6.創(chuàng)建訂單

    // 創(chuàng)建一個(gè)切片,存儲(chǔ)所有的商品ID
    productIds := make([]int, 0)
    for _, value := range productData {
        productIds = append(productIds, value["id"])
    }

    // 1.檢查訂單商品ID是否有效 +++++++++++++++++++++++++++++++++++++++++ Start
    // 創(chuàng)建一個(gè)通道,傳輸檢查錯(cuò)誤信息
    checkProductByIdFuncRet := make(chan bool, 10)
    // 判斷配置是否啟用子協(xié)程
    if subGoroutine {
        go checkProductById(productData, checkProductByIdFuncRet)
    } else {
        checkProductById(productData, checkProductByIdFuncRet)
    }
    // 1.檢查訂單商品ID是否有效 +++++++++++++++++++++++++++++++++++++++++ End

    // 2.檢查訂單商品ID的數(shù)量是否足夠本次創(chuàng)建訂單 ++++++++++++++++++++++++++ Start
    // 創(chuàng)建一個(gè)通道,傳輸檢查錯(cuò)誤信息
    checkProductNumByIdFuncRet := make(chan bool, 10)
    // 判斷配置是否啟用子協(xié)程
    if subGoroutine {
        go checkProductNumById(productData, checkProductNumByIdFuncRet)
    } else {
        checkProductNumById(productData, checkProductNumByIdFuncRet)
    }
    // 2.檢查訂單商品ID的數(shù)量是否足夠本次創(chuàng)建訂單 ++++++++++++++++++++++++++ End

    // 3.檢查優(yōu)惠券是否可用 ++++++++++++++++++++++++++++++++++++++++++++ Start
    // 創(chuàng)建一個(gè)通道,傳輸檢查錯(cuò)誤信息
    checkCouponFuncRet := make(chan int, 10)
    // 判斷配置是否啟用子協(xié)程
    if subGoroutine {
        go checkCoupon(couponId, checkCouponFuncRet)
    } else {
        checkCoupon(couponId, checkCouponFuncRet)
    }
    // 3.檢查優(yōu)惠券是否可用 ++++++++++++++++++++++++++++++++++++++++++++ End

    // 計(jì)算每個(gè)商品的訂單價(jià)格(這里可能是查詢商品的最新單價(jià)*數(shù)量) ++++++++++++++ Start
    // 創(chuàng)建一個(gè)通道,傳輸每個(gè)商品的訂單價(jià)格
    getOrderProductAmountByIdFuncRet := make(chan int, 10)
    // 判斷配置是否啟用子協(xié)程
    if subGoroutine {
        go getOrderProductAmountById(productData, getOrderProductAmountByIdFuncRet)
    } else {
        getOrderProductAmountById(productData, getOrderProductAmountByIdFuncRet)
    }
    // 計(jì)算每個(gè)商品的訂單價(jià)格(這里可能是查詢商品的最新單價(jià)*數(shù)量) ++++++++++++++ End

    // 計(jì)算訂單金額 +++++++++++++++++++++++++++++++++++++++++++++++++++ Start
    // 創(chuàng)建一個(gè)通道,傳輸訂單金額
    getOrderAmountFuncRet := make(chan int, 10)
    // 判斷配置是否啟用子協(xié)程
    if subGoroutine {
        go getOrderAmount(getOrderProductAmountByIdFuncRet, checkCouponFuncRet, getOrderAmountFuncRet)
    } else {
        getOrderAmount(getOrderProductAmountByIdFuncRet, checkCouponFuncRet, getOrderAmountFuncRet)
    }
    // 計(jì)算訂單金額 +++++++++++++++++++++++++++++++++++++++++++++++++++ Start

    // 遍歷通道數(shù)據(jù),有false就輸出錯(cuò)誤信息
    for v := range checkProductByIdFuncRet {
        if !v {
            fmt.Println("有無效的商品,請(qǐng)重新下單")
            // 發(fā)送錯(cuò)誤信息后,結(jié)束主goroutine,子goroutine也會(huì)自動(dòng)結(jié)束
            return
        }
    }
    for v := range checkProductNumByIdFuncRet {
        if !v {
            fmt.Println("商品數(shù)量不足,請(qǐng)重新下單")
            // 發(fā)送錯(cuò)誤信息后,結(jié)束主goroutine,子goroutine也會(huì)自動(dòng)結(jié)束
            return
        }
    }

    // 代碼執(zhí)行到這里,說明錯(cuò)誤檢查都通過了,所有需要計(jì)算的業(yè)務(wù)邏輯也都計(jì)算完成了,
    // 該拿著計(jì)算好的數(shù)據(jù)直接寫入數(shù)據(jù)庫完成訂單創(chuàng)建了,這一步就不需要啟動(dòng)子協(xié)程了
    create(getOrderAmountFuncRet)

    // 程序開始運(yùn)行時(shí)間
    subtract := time.Now().Sub(startDate)
    var subGoroutineMsg string
    if subGoroutine {
        subGoroutineMsg = "已啟用子協(xié)程"
    } else {
        subGoroutineMsg = "未啟用子協(xié)程"
    }
    fmt.Printf("結(jié)束! [%v],程序耗費(fèi)時(shí)間:%v\n", subGoroutineMsg, subtract)
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容