# Golang 學習筆記
Context 介紹
---
context ctx 在 golang中非常常見,也是相較於其他程式語言中非常獨特的特色
用途:**處理多個 goroutine 的情況,特別是用來送出取消或結束的 signal。**
context 可傳入 Goroutine, 再透過 `Done()` 取得 signal,一旦執行Done 的 Channel 關閉,Goroutine便會關閉並 return。
(即 父層的 `Context` 關閉其 Done channel 之後,子層的 Done channel 則會自動關閉。)
#### 基本規則
- 傳入context時應為第一個參數
- **不要傳遞nil的context**, `context.TODO()` instead
- 應只傳遞 scope 內能見/該請求之資料 (request-scoped data),不要傳入 optional parameters 到函式中。
- 同個 Context 在不同的 goroutine 之間傳遞是 thread safe 的 (換句話說,多個 goroutines 中同時使用 Context 是安全的)
```go
func DoSomething(ctx context.Context, arg Arg) error {
// ... use ctx ...
}
```
Context 方法
---
1. `context.Background()` : 回傳一個不是 `nil` 的 empty Context。**不會被取消 (canceled)**,**無值、也不會deadline**
2. `context.TODO` : 回傳一個不是 `nil` 的 empty Context。通常在無其他context可用或無法取得時使用。
3. `context.WithCancel()` : 回傳 Context 物件和 CancelFunction。這個 Context 的 Done channel 會在 cancel function 被呼叫到時關閉,或是父層的 Done channel 關閉時亦會關閉。
- 重複呼叫 cancel() 不會有任何效果
- context 建議當成函式或 goroutine 的參數傳入,並且命名為 ctx,並**不建議把它保存在 struct 中**
```go
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
```
example:
```go
func main() {
root := context.Background()
ctx, cancel := context.WithCancel(root)
defer cancel()
rand.Seed(time.Now().UnixNano())
if rand.Intn(2) == 0 {
cancel() // close context
}
select {
case <-ctx.Done():
fmt.Println("context closed")
default:
fmt.Println("hello")
}
}
```
4. `context.WithDeadline()` : 在 `context.WithDeadline()` 中可以指定一個時間(time.Time)當作 deadline,一旦時間到時,就會自動觸發 cancel
- context.WithDeadline() 同樣會回傳 cancel,因此也可以主動呼叫 cancel
- 如果父層的 context 被 cancel 的話,子層的 context 也會一併被 cancel
```go
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
```
5. `context.WithTimeout()` : api常用之超過一定的時間後就會停止該 function
```go
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
```
- `context.WithTimeout()` 的用法和 `context.WithDeadline()` 幾乎相同,差別只在於 WithTimeout() 帶入的參數是時間區間(time.Duration)
- 實際上,`WithTimeout()` 的底層仍然是呼叫 `WithDeadline()`
6. `context.WithValue()` : 在context中以key-value方式帶入傳遞參數
- 注意層層傳遞之順序問題
```go
func WithoutCancel(parent Context) Context
```
example:
```golang
func main() {
ctx, cancel := context.WithCancel(context.Background())
ctx1 := context.WithValue(ctx, "hello", "world")
ctx2 := context.WithValue(ctx1, "foo", "bar")
cancel()
select {
case <-ctx2.Done():
fmt.Println(ctx2.Value("hello"))
fmt.Println(ctx2.Value("foo"))
}
}
```
parent Context為第一參數,第二參數為key,第三個參數val為key對應的值。使用此函式可把資料放入context在goroutine中傳遞。
上面建立三個Context階層依序為ctx -> ctx1 -> ctx2。使用Context.WithValue分別在不同的Context放入值,可看到最末階的ctx2可取得parnet Context塞入的值。此外也可看到parent Context若取消其derived Context也會被關閉。
(此篇為各筆記之整理與個人使用心得,非原創內容)