本文目錄一覽:
golang之context詳解
為什麼需要context
在go伺服器中,對於每個請求的request都是在單獨的goroutine中進行的,處理一個request也可能設計多個goroutine之間的交互, 使用context可以使開發者方便的在這些goroutine里傳遞request相關的數據、取消goroutine的signal或截止日期
在並發程序中,由於超時、取消操作或者一些異常情況,往往需要進行搶佔操作或者中斷後續操作。熟悉channel的朋友應該都見過使用done channel來處理此類問題。比如以下這個例子:
上述例子中定義了一個buffer為0的channel done, 子協程運行著定時任務。如果主協程需要在某個時刻發送消息通知子協程中斷任務退出,那麼就可以讓子協程監聽這個done channel,一旦主協程關閉done channel,那麼子協程就可以推出了,這樣就實現了主協程通知子協程的需求。這很好,但是這也是有限的。
如果我們可以在簡單的通知上附加傳遞額外的信息來控制取消:為什麼取消,或者有一個它必須要完成的最終期限,更或者有多個取消選項,我們需要根據額外的信息來判斷選擇執行哪個取消選項。
考慮下面這種情況:假如主協程中有多個任務1, 2, …m,主協程對這些任務有超時控制;而其中任務1又有多個子任務1, 2, …n,任務1對這些子任務也有自己的超時控制,那麼這些子任務既要感知主協程的取消信號,也需要感知任務1的取消信號。
如果還是使用done channel的用法,我們需要定義兩個done channel,子任務們需要同時監聽這兩個done channel。嗯,這樣其實好像也還行哈。但是如果層級更深,如果這些子任務還有子任務,那麼使用done channel的方式將會變得非常繁瑣且混亂。
我們需要一種優雅的方案來實現這樣一種機制:
上層任務取消後,所有的下層任務都會被取消;中間某一層的任務取消後,只會將當前任務的下層任務取消,而不會影響上層的任務以及同級任務。
這個時候context就派上用場了。我們首先看看context的結構設計和實現原理。
context介面
先看Context介面結構,看起來非常簡單。
}
Context介面包含四個方法:
Deadline返回綁定當前context的任務被取消的截止時間;如果沒有設定期限,將返回ok == false。
Done 當綁定當前context的任務被取消時,將返回一個關閉的channel;如果當前context不會被取消,將返回nil。
Err 如果Done返回的channel沒有關閉,將返回nil;如果Done返回的channel已經關閉,將返回非空的值表示任務結束的原因。如果是context被取消,Err將返回Canceled;如果是context超時,Err將返回DeadlineExceeded。
Value 返回context存儲的鍵值對中當前key對應的值,如果沒有對應的key,則返回nil。
可以看到Done方法返回的channel正是用來傳遞結束信號以搶佔並中斷當前任務;Deadline方法指示一段時間後當前goroutine是否會被取消;以及一個Err方法,來解釋goroutine被取消的原因;而Value則用於獲取特定於當前任務樹的額外信息。而context所包含的額外信息鍵值對是如何存儲的呢?其實可以想像一顆樹,樹的每個節點可能攜帶一組鍵值對,如果當前節點上無法找到key所對應的值,就會向上去父節點裡找,直到根節點。
emptyCtx
emptyCtx是一個int類型的變數,但實現了context的介面。emptyCtx沒有超時時間,不能取消,也不能存儲任何額外信息,所以emptyCtx用來作為context樹的根節點。
Background和TODO只是用於不同場景下: Background通常被用於主函數、初始化以及測試中,作為一個頂層的context,也就是說一般我們創建的context都是基於Background;而TODO是在不確定使用什麼context的時候才會使用。
用法 :
如何將任意Golang介面轉換為位元組數組
golang語言本身就是c的工具集,開發c的程序用到的大部分結構體,內存管理,攜程等,golang基本都有,他只是在這個基礎上又加了一些概念這裡說一個很小的問題,就是位元組數組轉string的問題,網上大部分都是這樣轉的(包括google上):string(p[:]),這個轉完了是有問題的,我們再來看一下string這個結構體:
struct String
{
byte* str;
intgo len;
};
這個結構體讓我想起了nginx的string,他是這樣定義的:
typedef struct {
size_t len;
u_char *data;
} ngx_str_t;
golang裡邊 string的概念其實不是以前遇到\0結尾的概念了,他其實就是一塊連續的內存,首地址+長度,上面那樣賦值,如果p裡邊有\0,他不會做處理這個時候,如果再對這個string做其他處理就可能出問題了,比如strconv.Atoi轉成int就有錯誤,解決辦法就是需要自己寫一個正規的轉換函數:
func byteString(p []byte) string {
for i := 0; i len(p); i++ {
if p[i] == 0 {
return string(p[0:i])
}
}
return string(p)
}
這樣就不會出問題了
java怎麼調用golang的介面
1 介面的定義與理解
介面是一個自定義類型,它是一組方法的集合。從定義上來看,介面有兩個特點。第一,介面本質是一種自定義類型,因此不要將golang中的介面簡單理解為C++/Java中的介面,後者僅用於聲明方法簽名。第二,介面是一種特殊的自定義類型,其中沒有數據成員,只有方法(也可以為空)。
介面是完全抽象的,因此不能將其實例化。然而,可以創建一個其類型為介面的變數,它可以被賦值為任何滿足該介面類型的實際類型的值。介面的重要特性是:
(1)只要某個類型實現了介面要的方法,那麼我們就說該類型實現了此介面。該類型的值可以賦給該介面的值;
(2)作為1的推論,任何類型的值都可以賦值給空介面interface{}
注意:這只是golang中介面的特性,為非所有類型的特性(介面是一種特殊的類型)。
介面的特性是golang支持鴨子類型的基礎,即「如果它走起來像鴨子,叫起來像鴨子(實現了介面要的方法),它就是一隻鴨子(可以被賦值給介面的值)」。憑藉介面機制和鴨子類型,golang提供了一種有利於類、繼承、模板之外的更加靈活強大的選擇。
2 例子
type Exchanger interface {
exchange()
}
type StringPair struct {
first, second string
}
type Point[2]int
func (sp *StringPair) exchange() {
sp.first, sp.second = sp.second, sp.first
}
func (p *Point) exchange() {
p[0], p[1] = p[1], p[0]
}
func exchangeThese(exchangers …Exchanger) {
for _, exchanger := range exchangers {
exchanger.exchange()
}
}
func main() {
pair1 := StringPair{“abc”,”def”}
pair2 := StringPair{“ghi”,”jkl”}
point := Point{5, 7}
fmt.Println(pair1, pair2, point)
pair1.exchange()
pair2.exchange()
point.exchange()
fmt.Println(pair1, pair2, point)
// exchangeThese(pair1, pair2) //wrong
exchangeThese(pair1, pair2)
fmt.Println(pair1, pair2)
}
運行結果
在本例中,自定義類型StringPair和Point指針實現了介面Exchanger所需的方法,因此該類型的值可以被賦值給介面的值。
另外,特別注意一點。如果使用exchangeThese(pair1,
pair2)會導致編譯錯誤(如下圖),正確寫法應當是exchangeThese(pair1,
pair2)。這是由於真正滿足介面Exchanger的類型是StringPair指針,而非StringPair。
在golang中,值接收者和指針接收者的方法集是不同的。只是golang會智能地解引用和取引用,使得二者的方法集看上去是一樣的。但是,在調用exchangeThese時,就凸顯出二者的不同了。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/196482.html