本段測(cè)試代碼的說(shuō)明
這里用go語(yǔ)言簡(jiǎn)單的寫了一個(gè)創(chuàng)建訂單的業(yè)務(wù)流程,目的是測(cè)試一下Go語(yǔ)言的goroutine的魅力,注意,只是測(cè)試目的,代碼并不存在任何實(shí)用性.
測(cè)試邏輯說(shuō)明
創(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)建訂單測(cè)試代碼中,已經(jīng)把上面的6個(gè)步驟,全部封裝成了獨(dú)立的函數(shù),每個(gè)函數(shù)內(nèi)都啟用了延時(shí)1秒的操作來(lái)模擬真實(shí)
情況下的一些時(shí)間的耗費(fèi);最終目的,是想要分別嘗試一下,用goroutine方式和不用goroutine方式的區(qū)別,和性能差異!
測(cè)試結(jié)果
這里是測(cè)試代碼的兩次運(yùn)行結(jié)果,從結(jié)果中可以看到,首先是程序的執(zhí)行過(guò)程的不同,再就是程序耗費(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
測(cè)試代碼
package main
import (
"fmt"
"time"
)
// 是否啟用子子協(xié)程 false:不啟用(就像我們寫PHP代碼一樣從上至下的順序運(yùn)行代碼) true:啟用(啟用多個(gè)協(xié)程同時(shí)處理不同的業(yè)務(wù)邏輯,相關(guān)聯(lián)的數(shù)據(jù)通過(guò)通道傳輸)
var subGoroutine bool = false
/**
@title 檢查商品是否有效
@params productData []map[string]int 商品數(shù)據(jù)
@params out chan<- bool 發(fā)送數(shù)據(jù)的通道,通道內(nèi)傳遞的值為是否有效 true:有效 false:無(wú)效
*/
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:無(wú)效
*/
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:無(wú)效
*/
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語(yǔ)句,初始化定義一個(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通道的使用,簡(jiǎn)單的寫一下創(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的表示購(gòu)買數(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("有無(wú)效的商品,請(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í)行到這里,說(shuō)明錯(cuò)誤檢查都通過(guò)了,所有需要計(jì)算的業(yè)務(wù)邏輯也都計(jì)算完成了,
// 該拿著計(jì)算好的數(shù)據(jù)直接寫入數(shù)據(jù)庫(kù)完成訂單創(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)
}