请高手帮忙看个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()

}


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

要回复问题请先登录注册