这个函数可能会导致 Goroutine 泄漏吗?

huangapple go评论84阅读模式
英文:

Is this func possible to cause goroutine leak

问题

func startTimer(ctx context.Context, intervalTime int) {
intervalChan := make(chan bool)
go func() {
for {
select {
case <-ctx.Done():
return
case <-time.After(time.Second * time.Duration(intervalTime)):
intervalChan <- true
}
}
}()

for {
    select {
    case <-ctx.Done():
        return
    case <-intervalChan:
        doSomething()
    }
}

}

你好,我写了上面的一个函数,想知道是否可能导致 goroutine 泄漏。

例如,第一个 select 语句向 intervalChan 发送了一个 true,然后第二个 select 语句从 ctx.Done() 接收到 Done 标志并返回。这个 goroutine 会一直阻塞吗?

英文:
func startTimer(ctx context.Context, intervalTime int) {
	intervalChan := make(chan bool)	
	go func() {
		for {
			select {
			case &lt;-ctx.Done():
				return
			case &lt;-time.After(time.Second * time.Duration(intervalTime)):
				intervalChan &lt;- true
			}
		}
	}()
	

	for {
		select {
		case &lt;-ctx.Done():
			return
		case &lt;-intervalChan:
			doSomething()
	}
}

Hi,I write a func as above and want to know is it possible to cause goroutine leak.

For example, the first select statement sends a true to intervalChan, then the second select statement receives Done flag from ctx.Done() and return. Will the goroutine be block forever?

答案1

得分: 1

我无法每次复制这种行为,但可能是某种泄漏。如果doSomething执行一些重计算,同时goroutine在intervalChan <- true上被阻塞,因为它无法推送到通道中。在doSomething执行完成并且上下文被取消之后,startTimer在goroutine之前退出,这将导致goroutine被阻塞,因为没有任何intervalChan的消费者。

go version go1.8.3 darwin/amd64

package main

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

func startTimer(ctx context.Context, intervalTime int) {
	intervalChan := make(chan bool)
	go func() {
		for {
			select {
			case <-ctx.Done():
				fmt.Println("Done from inside of goroutine.")
				return
			case <-time.After(time.Second * time.Duration(intervalTime)):
				fmt.Println("Interval reached.")
				intervalChan <- true
			}
		}
	}()

	for {
		select {
		case <-ctx.Done():
			fmt.Println("Done from startTimer.")
			return
		case <-intervalChan:
			time.Sleep(10 * time.Second)
			fmt.Println("Done")
		}
	}
}

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

	startTimer(ctx, 2)
}
英文:

I cannot replicate this behaviour every time but could be some leak. If doSomething do some heavy computation, meanwhile goroutine is blocked on intervalChan &lt;- true since it cannot push into the channel. After doSomething finish execution and context was cancelled, startTimer exists before goroutine and this will lead into blocked goroutine, because there isn't any consumer of intervalChan.

go version go1.8.3 darwin/amd64

package main

import (
	&quot;context&quot;
	&quot;fmt&quot;
	&quot;time&quot;
)

func startTimer(ctx context.Context, intervalTime int) {
	intervalChan := make(chan bool)
	go func() {
		for {
			select {
			case &lt;-ctx.Done():
				fmt.Println(&quot;Done from inside of goroutine.&quot;)
				return
			case &lt;-time.After(time.Second * time.Duration(intervalTime)):
				fmt.Println(&quot;Interval reached.&quot;)
				intervalChan &lt;- true
			}
		}
	}()

	for {
		select {
		case &lt;-ctx.Done():
			fmt.Println(&quot;Done from startTimer.&quot;)
			return
		case &lt;-intervalChan:
			time.Sleep(10 * time.Second)
			fmt.Println(&quot;Done&quot;)
		}
	}
}

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

	startTimer(ctx, 2)
}

答案2

得分: 1

你的第一个goroutine可能会被无限期地阻塞在intervalChan <- true这一行。为了能够取消该发送操作,将其放在另一个select块中:

go func() {
    for {
        select {
        case <-ctx.Done():
            return
        case <-time.After(time.Second * time.Duration(intervalTime)):
            select {
            case <-ctx.Done():
                return
            case intervalChan <- true:
            }
        }
    }
}()
英文:

The only place your first goroutine could be blocked indefinitely is in intervalChan &lt;- true. Put it in another select block to be able to cancel that send:

go func() {
	for {
		select {
		case &lt;-ctx.Done():
			return
		case &lt;-time.After(time.Second * time.Duration(intervalTime)):
			select {
			case &lt;-ctx.Done():
				return
			case intervalChan &lt;- true:
			}
		}
	}
}()

huangapple
  • 本文由 发表于 2017年9月14日 17:11:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/46214982.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定