Context应用

来源:互联网 发布:js 鼠标滑过div手型 编辑:程序博客网 时间:2024/06/09 13:42
package mainimport (    "context"    "fmt"    "math/rand"    "os"    "os/signal"    "sync"    "syscall"    "time")func doFunc1(ctx context.Context, prompt string, wg *sync.WaitGroup) {    defer func() {        <-time.After(time.Duration(rand.Intn(5000)) * time.Millisecond)        fmt.Printf("%v is leaving.\n", prompt)        wg.Done()    }()    if "uno" == prompt {        wg.Add(1)        newCtx, _ := context.WithCancel(ctx)        go doFunc1(newCtx, "tres", wg)    }    intv := time.NewTicker(time.Second + time.Duration(rand.Intn(1000))*time.Millisecond)    for {        select {        case <-intv.C:            fmt.Printf("<%v>tick tick\n", prompt)        case <-ctx.Done():            return        default:        }    }}func main() {    rand.Seed(time.Now().UnixNano())    defer fmt.Println("bye")    ctx, doCancel := context.WithCancel(context.Background())    var wg sync.WaitGroup    wg.Add(2)    go doFunc1(ctx, "uno", &wg)    go doFunc1(ctx, "dos", &wg)    sigCh := make(chan os.Signal, 1)    signal.Notify(sigCh, os.Kill, os.Interrupt, syscall.SIGTERM)    <-sigCh    fmt.Println("closing")    doCancel()    timeoutCtx, _ := context.WithTimeout(context.Background(), 3*time.Second)    doneCh := make(chan struct{}, 1)    go func() {        defer fmt.Println("waited 2000.")        wg.Wait()        close(doneCh)    }()    select {    case <-doneCh:        fmt.Printf("waited done.\n")    case <-timeoutCtx.Done():        fmt.Printf("done error: %v\n", timeoutCtx.Err())    }}
  1. 带“嵌套”的go呼叫. context的cancel行为是带传递性的transitive
  2. 就算有呼叫者的主动cancel, 也需要子过程主动去查询“自己是否被cancel掉”
  3. 如果使用sync.WaitGroup的Wait, 可能存在永远的等待.

源码分析: (1.9.2)
Done()完成之后Err()的返回值属于以下两个:

// Canceled is the error returned by Context.Err when the context is canceled.var Canceled = errors.New("context canceled")// DeadlineExceeded is the error returned by Context.Err when the context's// deadline passes.var DeadlineExceeded error = deadlineExceededError{}type deadlineExceededError struct{}

注: error在golang中是一个接口:仅需要实现Error()string即可.

// propagateCancel arranges for child to be canceled when parent is.func propagateCancel(parent Context, child canceler) {    if parent.Done() == nil {        return // parent is never canceled    }    if p, ok := parentCancelCtx(parent); ok {        p.mu.Lock()        if p.err != nil {            // parent has already been canceled            child.cancel(false, p.err)        } else {            if p.children == nil {                p.children = make(map[canceler]struct{})            }            p.children[child] = struct{}{}        }        p.mu.Unlock()    } else {        go func() {            select {            case <-parent.Done():                child.cancel(false, parent.Err())            case <-child.Done():            }        }()    }}

建立context对象初期就会去创建一个goroutine, 用select挂在parent和自己的done channel上。

// A canceler is a context type that can be canceled directly. The// implementations are *cancelCtx and *timerCtx.type canceler interface {    cancel(removeFromParent bool, err error)    Done() <-chan struct{}}

WithCancel除了创建对象也要返回canceler, canceler就是对 cancelContext对象上调用cancel方法:
cancel之后总是会标记error, 所以canceler可以重复调用。

原文链接:
https://siadat.github.io/post/context

原创粉丝点击