Go并发模式之 防止goroutine泄漏

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013862108/article/details/88629715
goroutine 有以下几种方式被终止:
1。 当他完成了它的工作。
2。 因为不可恢复的错误, 它不能继续工作
3。 当他被告知 需要终止工作。

我们可以简单的使用前两种方法, 因为这两种方法隐含在你的算法中, 但"取消工作" 又是怎样工作的呢?

例如:这样情况: 子goroutine 是否该继续执行可能是以许多其他goroutine 状态的认知为基础的。

通常是 main goroutine 具有这种语境知识能够告诉其子goroutine 终止。

下面看一个 泄漏的例子/ (goroutine 没有进行死亡处理的例子)
func main() {
	doWork := func(strings <-chan string) <-chan interface {} {
		completed := make(chan interface{})
		go func() {
			defer fmt.Println("doWork exited.")
			defer close(completed)
			for s := range strings{
				//做一些有趣的事
				fmt.Println(s)
			}

		}()
		return completed
	}

	doWork(nil)
	//也许这里有其他操作需要执行
	fmt.Println("Done.")
}
*/
/**
分析: doWork 的goroutine 在整个生命周期中都保留在内存中;显然不合理,传入的是nil 应该是更早的结束这个goroutine 才对。
怎么解决呢,使用 done channel
分析: doWork 的goroutine 在整个生命周期中都保留在内存中;显然不合理,传入的是nil 应该是更早的结束这个goroutine 才对。
怎么解决呢,使用 done channel
func main(){
	doWork := func(done <-chan interface{}, strings <-chan string) <-chan interface{} {
		terminated := make(chan interface{})
		go func() {
			defer fmt.Println("doWork exited.")
			defer close(terminated)

			for{
				select {
					case s := <-strings :
						//做一些有趣的事情
						fmt.Println(s)
					case <-done :
						return
				}
			}
		}()

		return terminated
	}

	done := make(chan interface{})
	terminated := doWork(done, nil)

	go func() {
		//一秒后取消 dowork 里边的 goroutine
		time.Sleep(1 * time.Second)
		fmt.Println("Cancelling doWork goroutine...")
		close(done)
	}()

	<-terminated
	fmt.Println("Done.")
}
//Cancelling doWork goroutine...
//doWork exited.
//Done.

*/
/**
分析: 尽管我们给我们的字符串 channel 中传递了nil, 我们的goroutine仍然成功退出。 通过 1s 后 关闭doWork 中 goroutine 成功消除了我们的
goroutine 泄漏
分析: 尽管我们给我们的字符串 channel 中传递了nil, 我们的goroutine仍然成功退出。 通过 1s 后 关闭doWork 中 goroutine 成功消除了我们的
goroutine 泄漏
这个例子 是: doWork 的 goroutine 从某个channel(作为参数传进来) 中 for 读取

下面这个例子: doWork  中的goroutine 向某个channel() 中写入(for方式)数据
func main(){
	newRandStream := func() <-chan int{
		randStream := make(chan int)
		go func() {
			defer fmt.Println("newRandStream closure exited.")
			defer close(randStream)
			for{
				randStream <- rand.Int()
			}
		}()
		return randStream
	}

	randStream := newRandStream()
	fmt.Println("3 random ints:")
	for i:=1; i<=3; i++{
		fmt.Printf("%d: %d\n", i, <-randStream)
	}

}
//
//3 random ints:
//1: 5577006791947779410
//2: 8674665223082153551
//3: 6129484611666145821
*/

/**
问题: 在三次迭代后,我们的goroutine试图将下一个随机数发送到不需要的被读取的 channel. 我们没有告诉生产者它可以停止了。

解决方案: 像接收案例一样,为生产者提供一个通知他 退出 的 done channel:
问题: 在三次迭代后,我们的goroutine试图将下一个随机数发送到不需要的被读取的 channel. 我们没有告诉生产者它可以停止了。

解决方案: 像接收案例一样,为生产者提供一个通知他 退出 的 done channel:
func main(){
	newRandStream := func(done <-chan interface{}) <-chan int {
		randStream := make(chan int)
		go func() {
			defer fmt.Println("newRandStream closure exited. ")
			defer close(randStream)

			for{
				select {
					case randStream <-rand.Int() :
						case <-done :
							return
				}
			}
		}()

		return randStream
	}

	done := make(chan interface{})
	randStream := newRandStream(done)
	fmt.Println(" 3 random ints:")
	for i:=1; i<=3; i++ {
		fmt.Printf("%d: %d\n", i, <-randStream)
	}
	close(done)

	//正在模拟进行的工作
	time.Sleep(1 * time.Second)
}
*/
/**
为了goroutine 不泄漏, 约定: 如果goroutine 负责创建 goroutine,他也负责 确保可以停止 goroutine
 */
为了goroutine 不泄漏, 约定: 如果goroutine 负责创建 goroutine,他也负责 确保可以停止 goroutine

猜你喜欢

转载自blog.csdn.net/u013862108/article/details/88629715