跳至主要內容
yomihon 書庫
整體狀況設定

鍵盤快捷鍵

⌘K / Ctrl K
搜尋
Esc
關閉搜尋或篩選
/
跳到導覽篩選(單鍵開啟時)
[
收合或展開側欄(單鍵開啟時)

關掉之後,/ 和 [ 都只會照瀏覽器原本的方式輸入;⌘K 與 Esc 不受影響。

設定
esc

↑↓ 選擇 ↵ 搜尋 esc 關閉

G06 select 與逾時

lesson檔案變更於
檔案變更於
類型
lesson
語言
zh-Hant
在 Obsidian 開啟留下自己的想法
檔案變更於
類型
lesson
語言
zh-Hant
在 Obsidian 開啟留下自己的想法

等待結果時,可以同時等三件事:值抵達、呼叫者取消、時間到。select 選擇一個可進行的分支;沒有分支就緒,也沒有 default 時,才會阻塞。1

package main

import (
	"context"
	"errors"
	"fmt"
	"time"
)

func receive(ctx context.Context, values <-chan int, timeout time.Duration) (int, error) {
	timer := time.NewTimer(timeout)
	defer timer.Stop()
	select {
	case value, ok := <-values:
		if !ok {
			return 0, errors.New("channel 已關閉")
		}
		return value, nil
	case <-ctx.Done():
		return 0, ctx.Err()
	case <-timer.C:
		return 0, errors.New("等待逾時")
	}
}

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	values := make(chan int)

	_, err := receive(ctx, values, 20*time.Millisecond)
	fmt.Println(err)
}

這次沒有發送者,也沒有人提早取消,所以會印出 等待逾時。計時器至少等指定時間才觸發,不保證函式恰好在第 20 毫秒返回。2

第一個 case 有優先權嗎?

把 values 改成容量為一,呼叫 receive 前先送入 7。如果值與計時器都已就緒,寫在最前面的接收分支是否一定勝出?再加一個 default,又會如何改變等待?

查看分支選擇規則

多個分支就緒時,select 以均勻的偽隨機方式選一個,不保證取到值,也不保證逾時優先。把某個 case 移到最上面,不會提高它的優先權。

default 只在沒有其他分支可進行時執行,而且立即執行。用它印出「逾時」,表示的其實是「此刻沒有結果」,並沒有等過指定時間。

逾時只結束這次等待

這個 receive 不會停止外部 producer。若結果來自另一個 goroutine,呼叫端還要取消它的 context,並等待它收尾,做法見 G04 取消不再需要的工作。需要讓多層函式共用同一個期限時,可用 context.WithTimeout(另開分頁),各層都接受並遵守該 context。

作者標記的狀態 ready
設為 archived

設為 archived 之後,這裡不再有回到目前狀態的路。