最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

重學(xué)Go語言之如何使用Context

 更新時(shí)間:2023年07月31日 11:52:12   作者:程序員讀書  
Context,中文也叫做上下文,Go語言在1.7版本中新增的context包中定義了Context,下面我們就來一起看看如何在Go語言中使用Context吧

我們知道在開發(fā)Go應(yīng)用程序時(shí),尤其是網(wǎng)絡(luò)應(yīng)用程序,需要啟動(dòng)大量的Goroutine來處理請(qǐng)求:

不過Goroutine被創(chuàng)建之后,除非執(zhí)行后正常退出或者觸發(fā)panic退出,Go并沒有提供在一個(gè)Goroutine中關(guān)閉另一個(gè)Goroutine的機(jī)制。

有沒有一種方法,可以從一個(gè)Goroutine中通知另一個(gè)Goroutine退出執(zhí)行呢?這時(shí)候就該Context登場(chǎng)了!

什么是Context

Context,中文叫做上下文,Go語言在1.7版本中新增的context包中定義了ContextContext本質(zhì)是一個(gè)接口,這個(gè)接口一共定義了四個(gè)方法:

type?Context?interface?{
?Deadline()?(deadline?time.Time,?ok?bool)
?Done()?<-chan?struct{}
?Err()?error
?Value(key?any)?any
}
  • Dateline():獲取定時(shí)關(guān)閉的時(shí)間。
  • Done():從一個(gè)channel獲取關(guān)閉的信號(hào)
  • Err():獲取錯(cuò)誤信息。
  • Value():根據(jù)keyContext取值

為什么要使用Context

為什么要使用Context?或者說設(shè)計(jì)Context的目的是什么?

想像一下這樣的場(chǎng)景,當(dāng)用戶開啟瀏覽器訪問我們的Web服務(wù)時(shí),我們可能會(huì)開啟多個(gè)Goroutine來處理用戶的請(qǐng)求,這些Goroutine需要讀取不同的資源,最終返回給用戶,但如果用戶在我們的Web服務(wù)還沒處理完成就關(guān)閉瀏覽器,斷開連接,而此時(shí)Web不知道用戶已經(jīng)關(guān)閉請(qǐng)求,仍然在處理并返回最終并不會(huì)被接收的數(shù)據(jù)。

Context設(shè)計(jì)的目的就是可以從上游的Goroutine發(fā)送信息給下游的Goroutine,回到處理用戶請(qǐng)求的場(chǎng)景,當(dāng)處理請(qǐng)求的Goroutine發(fā)現(xiàn)用戶斷開連接時(shí),通過Context發(fā)送停止執(zhí)行的信息,而下游的Goroutine得到停止信號(hào)時(shí)就返回,避免資源的浪費(fèi)。

概括起來,Context的作用主要體現(xiàn)在兩個(gè)方面:

  • Goroutine之間傳遞關(guān)閉信息,定時(shí)關(guān)閉,超時(shí)關(guān)閉,手動(dòng)關(guān)閉。
  • Goroutine之間傳遞數(shù)據(jù)。

Context的使用

下面我們來講講Context的基本使用。

創(chuàng)建Context

任何上下文都是從一個(gè)空白的Context開始的,創(chuàng)建一個(gè)空白的Context有兩種方式:

使用context.Background()

ctx?:=?context.Backgroud()

使用contenxt.TODO():

ctx?:=?context.TODO()

當(dāng)然大部分時(shí)候,我們不需要自己創(chuàng)建一個(gè)空白的Context,比如在處理HTTP請(qǐng)求時(shí):

http.HandleFunc("/hello",?func(w?http.ResponseWriter,?r?*http.Request)?{
?ctx?:=?r.Context()
})

上面的代碼中,可以從http.Request中獲取Context實(shí)例,而http.Request的實(shí)際上也是調(diào)用context.Backgroud():

//net/http/request.go
func?(r?*Request)?Context()?context.Context?{
?if?r.ctx?!=?nil?{
??return?r.ctx
?}
?return?context.Background()
}

實(shí)例講解

為了講解Context是如何在Goroutine之間傳遞信號(hào)與數(shù)據(jù),我們通過下面的案例進(jìn)行說明:

func?ReadFile(ctx?context.Context,?fileName?string,?result?chan<-?[]byte)?{
?file,?err?:=?os.Open(fileName)
?if?err?!=?nil?{
??return
?}
?totalResult?:=?make([]byte,?0)
?for?{
??select?{
??case?<-ctx.Done():
???result?<-?[]byte{}
???return
??default:
??}
??b?:=?make([]byte,?1024)
??_,?err?:=?file.Read(b)
??if?err?==?io.EOF?{
???totalResult?=?append(totalResult,?b...)
???break
??}
??totalResult?=?append(totalResult,?b...)
?}
?result?<-?totalResult
}

在上面的程序中,我們調(diào)用ContextDone()方法,該方法會(huì)返回一個(gè)Channel,而使用select語句則可以讓我們?cè)谔幚順I(yè)務(wù)的同時(shí),監(jiān)聽上游Goroutine是否有傳遞取消執(zhí)行的信息。

利用Context傳遞停止信號(hào)

空白的Context并不能發(fā)揮什么作用,要達(dá)到手動(dòng)取消執(zhí)行的目的,需要調(diào)用context包下的WithCancel函數(shù)進(jìn)行封裝,封裝返回一個(gè)新的context以及一個(gè)取消的句柄cancel函數(shù):

ctx?:=?context.Background()
ctx,?cancel?:=?context.WithCancel(ctx)

下面是完整的使用方法:

package?main
import?(
?"context"
?"fmt"
?"io"
?"os"
?"time"
)
func?main()?{
?ctx?:=?context.Background()
?ctx,?cancel?:=?context.WithCancel(ctx)
?result?:=?make(chan?[]byte)
?go?ReadFile(ctx,?"./test.tar",?result)
?time.Sleep(100?*?time.Millisecond)
?cancel()
?fmt.Println(<-result)
}

在上面的程序中,我們創(chuàng)建一個(gè)Context之后,傳給了ReadFile函數(shù),并且在暫停100毫秒后調(diào)用cancel()函數(shù),達(dá)到手動(dòng)取消另一個(gè)Goroutine的目的。

給Context設(shè)置一個(gè)截止時(shí)間

除了手動(dòng)取消,也可以調(diào)用context包下的WithDeadline()函數(shù)給Context加一個(gè)截止時(shí)間,這樣在某個(gè)時(shí)間點(diǎn),Context會(huì)自動(dòng)發(fā)出取消信號(hào):

afterTime?:=?time.Now().Add(30?*?time.Millisecond)
ctx,?cancel?:=?context.WithDeadline(context.Background(),?afterTime)

下面是完整的示例:

package?main
import?(
?"context"
?"fmt"
?"io"
?"os"
?"time"
)
func?main()?{
?afterTime?:=?time.Now().Add(30?*?time.Millisecond)
?ctx,?cancel?:=?context.WithDeadline(context.Background(),?afterTime)
?defer?cancel()
?result?:=?make(chan?[]byte)
?go?ReadFile(ctx,?"./test.tar",?result)
?fmt.Println(<-result)
}

給Context一個(gè)超時(shí)限制

調(diào)用context包下的WithTimeout()函數(shù)可以為Context加一個(gè)超時(shí)限制,這對(duì)于我們編寫超時(shí)控制程序非常有幫助:

ctx,?cancel?:=?context.WithTimeout(context.Background(),?10*time.Millisecond)

下面是完整的調(diào)用程序:

package?main
import?(
?"context"
?"fmt"
?"io"
?"os"
?"time"
)
func?main()?{
?ctx,?cancel?:=?context.WithTimeout(context.Background(),?10*time.Millisecond)
?defer?cancel()
?result?:=?make(chan?[]byte)
?go?ReadFile(ctx,?"./test.tar",?result)
?fmt.Println(<-result)
}

使用Context傳遞數(shù)據(jù)

調(diào)用context包下的WithValue()函數(shù)可以生成一個(gè)攜帶數(shù)據(jù)的Context,這個(gè)機(jī)制方便我們跟蹤一個(gè)處理流程中的Goroutine

?ctx,?cancel?:=?context.WithValue(context.Background(),?"testKey","testValue")

下游的Goroutine就可以通過ContextValue()函數(shù)來獲取上游傳遞下來的值了。

func?MyGoroutine(ctx?context.Context){
?ctx.Value("testKey")
}

使用Context的幾點(diǎn)建議

Context不要放在結(jié)構(gòu)體中,需要以參數(shù)方式傳遞。

Context作為函數(shù)參數(shù)時(shí),一般放在第一位,作為函數(shù)的第一個(gè)參數(shù)。

使用 context.Background函數(shù)生成根節(jié)點(diǎn)的Context。

Context 要傳值必要的值,不要什么都傳。

Context 是多協(xié)程安全的,可以在多個(gè)協(xié)程中使用。

小結(jié)

好了,至此,你應(yīng)該對(duì)Context有所了解了吧,總的來說,通過Context可以做到:

  • 手動(dòng)控制下游Goroutine取消執(zhí)行。
  • 定時(shí)控制下游Goroutine取消執(zhí)行。
  • 超時(shí)控制下游Goroutine取消執(zhí)行。
  • 傳遞數(shù)據(jù)給下游的Goroutine。

到此這篇關(guān)于重學(xué)Go語言之如何使用Context的文章就介紹到這了,更多相關(guān)Go Context內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

菏泽市| 广平县| 枣强县| 无锡市| 阳原县| 宁城县| 新安县| 屏东县| 卢龙县| 乌审旗| 阿拉善左旗| 泰来县| 宜春市| 民权县| 鹿泉市| 南投县| 瑞丽市| 凌源市| 东光县| 阿尔山市| 紫金县| 顺平县| 旬阳县| 翁牛特旗| 苍梧县| 建水县| 青田县| 镇平县| 姜堰市| 静海县| 遵义县| 万源市| 通海县| 陕西省| 水富县| 中江县| 天津市| 磴口县| 蒙城县| 宜昌市| 岳阳县|