3.1 go context代码示例

context.WithCancel
返回两个有关联的对象,ctx与cancel,调用cancel发送一个空struct给ctx,ctx一旦接收到该对象后,就终止goroutine的执行;
ctx是线程安全的,可以同时传递给多个goroutine,触发cancel时,取消所有goroutine的执行
package main

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

func testContext(){
    ctx,cancel := context.WithCancel(context.Background())
    go d1(ctx)
    go d2(ctx)
    
    time.Sleep(7*time.Second)
    cancel()
}

func d1(ctx context.Context){
    i:=0
    for {
        time.Sleep(1*time.Second)
        i++
        select {
            case <- ctx.Done():
                fmt.Println("d1 over")
                return
                
            default:
                fmt.Println("d1 ",i)
        }
    }
}

func d2(ctx context.Context){

    fmt.Println("d2 start")
    <- ctx.Done()
    fmt.Println("d2 over")
}

func main(){
    testContext()
    fmt.Println("main over")
}

输出

d2 start
d1  1
d1  2
d1  3
d1  4
d1  5
d1  6
main over
context适用于这样的情况,你需要持续处理请求,有多个case分支,case分支中持续处理着数据,
select 之前的代码不会被终止,下面的这种写法,会终止return之后的代码、以及其他case分支的代码
case <- ctx.Done():
     fmt.Println("d1 over")
     return

使用场景举例
package main

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

func d1(i *int)  chan int{
    var cc = make(chan int)
    go func() {
        for {
            time.Sleep(1*time.Second)
            *i = *i + 1
            cc <- *i
        }
    }()
    return cc
}

func textContext(cc chan int,ctx context.Context)  {
    for {
        select {
        case <- ctx.Done():
            fmt.Println("context done ")
            return
        case n:= <- cc :
            fmt.Println(n)
        }
    }
    fmt.Println("wg done")
}

func main() {
    ctx,cancel := context.WithCancel(context.Background())


    i:= 0
    var cc = d1(&i)

    go textContext(cc,ctx)

    for {
        time.Sleep(1*time.Second)
        if i > 10 {
            cancel()
            break
        }
    }
}


 

context.WithTimeout

package main

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

func d1(ctx context.Context){
    i := 0
    for{
        time.Sleep(1*time.Second)
        select{
            case <- ctx.Done():
                fmt.Println("d1 over")
                return
            default:
                fmt.Println("d1:",i)
        }
    }
}

func test(){
    ctx,cancel := context.WithTimeout(context.Background(),5*time.Second)
    go d1(ctx)
    fmt.Println("begin sleep 10 sec")
    time.Sleep(10*time.Second)
    fmt.Println("10 sec over")
    cancel()
}


func main(){
    test()
}
[root@phoenix go]# go run cc.go 
begin sleep 10 sec
d1: 0
d1: 0
d1: 0
d1: 0
d1 over
10 sec over

  

无法中止正在执行中case分支

package main

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

func main() {
    ctx,cancel:= context.WithTimeout(context.Background(),5*time.Second)
    go timeOut(ctx)

    time.Sleep(10*time.Second)
    fmt.Println("强制唤醒")
    cancel()
    time.Sleep(5*time.Second)
    fmt.Println("主程序结束")
}

func timeOut(ctx context.Context)  {

    for{
        select {
        case <- ctx.Done():
            fmt.Println("context done")

        default:
            fmt.Println("沉睡100秒")
            time.Sleep(100*time.Second)

        }
    }
}

沉睡100秒这个case分支一旦开始执行,除非main协程结束,否则context是无法中止其执行的;5秒超时context发送了结束信号,但select有case未执行完,其channel处于阻塞状态,所以无法中止程序

沉睡100秒
强制唤醒
主程序结束
原文地址:https://www.cnblogs.com/perfei/p/11529122.html