请高手帮忙看个golang context的问题

package main

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

type CNode struct {
    Name     string
    incoming chan int
    ch       chan int
    ctx      context.Context
    cancel   context.CancelFunc
    wg       sync.WaitGroup
}

func NewCNode(ctx context.Context, name string) *CNode {
    n := &CNode{
        Name:     name,
        incoming: make(chan int, 100),
        ch:       make(chan int, 10),
        wg:       sync.WaitGroup{},
    }
    n.ctx, n.cancel = context.WithCancel(ctx)
    go n.run()

    for i := 0; i < 4; i++ {
        go n.handler()
    }
    return n
}

func (n *CNode) run() {
    //defer close(n.incoming)
    //defer n.wg.Done()

    n.wg.Add(1)

    i := 1
    for {
        i++
        select {
        default:
        case <-n.ctx.Done():
            fmt.Println(n.Name, "handler done", n.ctx.Err())
            n.wg.Done()
            return
        }
        n.incoming <- i
        time.Sleep(1 * time.Second)
    }
}

func (n *CNode) stop() {
    if n.cancel != nil {
        fmt.Println("stopnode")
        n.cancel()
        n.cancel = nil
    }
    n.wg.Wait()

    close(n.ch)
    close(n.incoming)
}

func (n *CNode) handler() {
    //defer n.wg.Done()

    n.wg.Add(1)

    for {
        //i := <-n.incoming
        select {
        case i := <-n.incoming:
            n.ch <- i * 10
        case <-n.ctx.Done():
            fmt.Println(n.Name, "handler done2", n.ctx.Err())
            n.wg.Done()
            return
        }

        //fmt.Println(n.Name, i)
    }
}
func main() {

    n1ctx, n1cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer n1cancel()

    n1 := NewCNode(n1ctx, "node1")
    time.Sleep(2 * time.Second)
    n1.stop()
}

以上代码只要52行的time.Sleep注释掉就会deadlock

===========================================

下午尝试了各种方法,仍然不行,看了golang官网关于context的用法,貌似没有我这样用的, 发现context和goroutine+channel一起使用,用来控制goroutine的关闭会有问题。 但从各种关于context的文章,也没有明确指出不能这么用,按照context的使用场景,这样使用也应该是没有问题的。

已邀请:

koalaone

赞同来自:

close(n.ch) close(n.incoming)

chan被close了,不能往里面写了

hubery

赞同来自:

n.wg.Done()---》 n.wg.Add(1) defer n.wg.Done()

CNode.ch 只有写的动作 没有读 当buffer写满了 就会block

winlin - 要浪就要够浪

赞同来自:

一般来说,context和WaitGroup就达到目标了,如果你用了context还要用chan,说明是误用了。不知道你到底想要达到什么目标?是子goroutine退出时通知其他的context也cancel吗?可以考虑context的树,也就是在context上新建出新的context,这种方式估计可以替代你的chan。

axlrose

赞同来自:

package main

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

type CNode struct { Name string incoming chan int ch chan int ctx context.Context cancel context.CancelFunc wg sync.WaitGroup }

func NewCNode(ctx context.Context, name string) *CNode { n := &CNode{ Name: name, incoming: make(chan int, 1), ch: make(chan int, 1), wg: sync.WaitGroup{}, } n.ctx, n.cancel = context.WithCancel(ctx) go n.run()

for i := 0; i < 4; i++ {
    go n.handler()
}
return n

}

func (n *CNode) run() { //defer close(n.incoming) //defer n.wg.Done()

n.wg.Add(1)

defer n.wg.Done()
i := 1
for {
    i++
    select {
    default:
    case <-n.ctx.Done():
        fmt.Println(n.Name, "handler done", n.ctx.Err())
        return
    }
    select {
    case n.incoming <- i:
        fmt.Println("incoming <- ", i)
    default:
        //          fmt.Println("incoming not ready: ", i)
    case <-n.ctx.Done():
        fmt.Println(n.Name, "AAAA handler done", n.ctx.Err())
        return
    }
    //      n.incoming <- i
    //      time.Sleep(300 * time.Millisecond)
}

}

func (n *CNode) stop() { if n.cancel != nil { fmt.Println("stopnode") n.cancel() n.cancel = nil } n.wg.Wait()

close(n.ch)
close(n.incoming)

}

func (n *CNode) handler() { //defer n.wg.Done()

n.wg.Add(1)
defer n.wg.Done()
for {
    //i := <-n.incoming
    select {
    case i := <-n.incoming:
        fmt.Printf("get incoming = %d\n", i)
        select {
        case n.ch <- i * 10:
            fmt.Printf("n.ch <- data\n")

        case <-n.ctx.Done():
            fmt.Println(n.Name, "BBBB handler done2", n.ctx.Err())
            return
        }

    case <-n.ctx.Done():
        fmt.Println(n.Name, "handler done2", n.ctx.Err())
        return
    }

    //fmt.Println(n.Name, i)
}

} func main() {

n1ctx, n1cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer n1cancel()

n1 := NewCNode(n1ctx, "node1")
//  time.Sleep(2 * time.Second)
n1.stop()

}

本人水平比较菜, 这个是你想要的效果吗? 

要回复问题请先登录注册