fuga goroutine

contorno

En Go, goroutine muy ligero, crear fácilmente miles de goroutine no es un problema, pero tenga en cuenta que si tantos aumento constante goroutine, pero no dejar de fumar, no liberan recursos en problemas. En este artículo se describe la escena real goroutine se filtró, y discutir cómo resolver el problema.

Análisis de la causa

La fuga razón goroutine (co-rutinas de fuga) puede tener la siguiente:

  • goroutine ya que el canal de lectura / escritura y salida de la terminal ha sido bloqueado, lo que resulta en goroutine recursos ocupados estado, no puede dejar de fumar
  • goroutine entrar en un bucle infinito, lo que resulta en la liberación de recursos ha sido incapaz de

goroutine terminado escena

Un goroutine terminar las siguientes situaciones:

  • Cuando un goroutine completó su trabajo
  • No procesado debido a un error ocurrió
  • Hay otra corrutina dilo a terminar

La fuga goroutine real

escena del Consumidor Productor

func main() {
	newRandStream := func() <-chan int {
		randStream := make(chan int)

		go func() {
			defer fmt.Println("newRandStream closure exited.")
			defer close(randStream)
			// 死循环:不断向channel中放数据,直到阻塞
			for {
				randStream <- rand.Int()
			}
		}()

		return randStream
	}

	randStream := newRandStream()
	fmt.Println("3 random ints:")

	// 只消耗3个数据,然后去做其他的事情,此时生产者阻塞,
	// 若主goroutine不处理生产者goroutine,则就产生了泄露
	for i := 1; i <= 3; i++ {
		fmt.Printf("%d: %d\n", i, <-randStream)
	}

	fmt.Fprintf(os.Stderr, "%d\n", runtime.NumGoroutine())
	time.Sleep(10e9)
	fmt.Fprintf(os.Stderr, "%d\n", runtime.NumGoroutine())
}

corrutina Producción en el bucle, la generación continua de los datos, el proceso de asociación de consumidores, que es la co-rutina principal sólo consumen tres de estos valores, entonces ya no es el principal consumo corrutina en el canal de datos, para hacer otras cosas. En este punto, la co-rutina de producción puesto en un canal de datos, pero no tiene corrutina consumir los datos, por lo que la producción corrutina bloqueado. En este momento, si no hay nadie en el canal de datos de consumo, la generación de una co-rutina es corrutina filtrado.
Solución
En general, para resolver el problema de la fuga de canal goroutine causado, sobre todo para ver si el bloqueo del canal goroutine, bloqueando el goroutine es normal, o puede causar corrutina nunca tienen la oportunidad de actuar. Si la co-rutina puede conducir a no tener la oportunidad de llevar a cabo, puede producir la pérdida de co-rutinas. Por lo tanto, cuando se crea una co-rutina que es necesario tener en cuenta la forma en la terminación.

Solución al problema general es que, cuando el extremo del hilo principal, el hilo para informar a la producción, post-producción de la rosca a ser informado, para limpiar el trabajo: dentro o fuera, o hacer algo de trabajo para limpiar el medio ambiente.

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)
	}

    // 通知子协程结束自己
    // done <- struct{}{}
	close(done)
	// Simulate ongoing work
	time.Sleep(1 * time.Second)
}

La co-rutina código anterior para ser notificado por el extremo de un canal, de modo que pueda limpiar el sitio. Corrutina evitar fugas. Corrutina notar el final del camino, el envío de una estructura vacía, de forma más sencilla es directamente cerca del canal. Como se muestra en la figura.

escena de la obra maestra

En este escenario, por lo general, el trabajo ha de ser dividido en varios trabajos, el trabajo para poner a cada niño cada goroutine para completar. En este momento, si no se maneja correctamente, es probable que se produzcan fugas goroutine. Echemos un vistazo a un ejemplo práctico:

// function to add an array of numbers.
func worker_adder(s []int, c chan int) {
	sum := 0
	for _, v := range s {
		sum += v
	}
	// writes the sum to the go routines.
	c <- sum // send sum to c
	fmt.Println("end")
}

func main() {
	s := []int{7, 2, 8, -9, 4, 0}

	c1 := make(chan int)
	c2 := make(chan int)

	// spin up a goroutine.
	go worker_adder(s[:len(s)/2], c1)
	// spin up a goroutine.
	go worker_adder(s[len(s)/2:], c2)

	//x, y := <-c1, <-c2 // receive from c1 aND C2
	x, _:= <-c1
	// 输出从channel获取到的值
	fmt.Println(x)

	fmt.Println(runtime.NumGoroutine())
	time.Sleep(10e9)
	fmt.Println(runtime.NumGoroutine())
}

El código de seguridad en la co-rutina principal, para una matriz dividida en dos partes, cada trabajador a dos co-rutina para calcular su valor, la co-rutina de dos canales por los resultados devueltos a la co-rutina principal. Sin embargo, en el código anterior, que sólo se ha recibido un canal de datos, lo que resulta en otros bloques de co-rutina durante la escritura de canal, la oportunidad nunca se ejecutó. Si ponemos este código en un servicio permanente, el aspecto es más evidente:

http escenarios de servidor

// 把数组s中的数字加起来
func sumInt(s []int, c chan int) {
	sum := 0
	for _, v := range s {
		sum += v
	}
	c <- sum
}

// HTTP handler for /sum
func sumConcurrent2(w http.ResponseWriter, r *http.Request) {
	s := []int{7, 2, 8, -9, 4, 0}

	c1 := make(chan int)
	c2 := make(chan int)

	go sumInt(s[:len(s)/2], c1)
	go sumInt(s[len(s)/2:], c2)

	// 这里故意不在c2中读取数据,导致向c2写数据的协程阻塞。
	x := <-c1

	// write the response.
	fmt.Fprintf(w, strconv.Itoa(x))
}

func main() {
	StasticGroutine := func() {
		for {
			time.Sleep(1e9)
			total := runtime.NumGoroutine()
			fmt.Println(total)
		}
	}

	go StasticGroutine()

	http.HandleFunc("/sum", sumConcurrent2)
	err := http.ListenAndServe(":8001", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

Si ejecuta el procedimiento anterior y entra en su navegador:

http://127.0.0.1:8001/sum

Y constantemente actualizar el navegador envía una petición para continuar, se puede ver el siguiente resultado:

2
2
5
6
7
8
9
10

Esta salida es el número de corrutina nuestro servidor HTTP, se puede ver: una vez por cada solicitud, la Asociación se ha incrementado un número de pases, y no será reducida. fuga Descripción corrutina se ha producido (fuga goroutine).

Solución de
una solución es, no importa en todo caso, debemos tener una co-rutina puede leer y escribir canal, y mucho corrutina no bloquear. El código cambia de la siguiente manera:

...
	x,y := <-c1,<-c2

	// write the response.
	fmt.Fprintf(w, strconv.Itoa(x+y))
...

Cómo depurar y encontrar goroutine fuga

tiempo de ejecución

Usted puede obtener el número de servicios en segundo plano corrutina por la función runtime.NumGoroutine (). Mirando cada uno de corrutina y aumentar o disminuir el número de cambios, podemos determinar si hay fugas goroutine ocurrió.

...
	fmt.Fprintf(os.Stderr, "%d\n", runtime.NumGoroutine())
	time.Sleep(10e9) //等一会,查看协程数量的变化
	fmt.Fprintf(os.Stderr, "%d\n", runtime.NumGoroutine())
...

pprof que confirmar locales filtró

Una vez que encontramos fugas goroutein, necesitamos confirmar el origen de la fuga.

import (
  "runtime/debug"
  "runtime/pprof"
)

func getStackTraceHandler(w http.ResponseWriter, r *http.Request) {
    stack := debug.Stack()
    w.Write(stack)
    pprof.Lookup("goroutine").WriteTo(w, 2)
}
func main() {
    http.HandleFunc("/_stack", getStackTraceHandler)
}

resumen

goroutine fuga ocurre a menudo debido a corrutinas bloqueados en el canal, o co-rutinas en el circuito, especialmente en algunos de los antecedentes de los servicios a residentes. Cuando se utiliza el canal y goroutine a la nota:

  • Y la búsqueda de lo bueno que el goroutine final cuando se crea goroutine
  • Cuando se utiliza el canal, hay que tener en cuenta a la hora de canales bloqueando el comportamiento probable corrutina
  • Por lo general, prestar atención a algunos escenarios de fugas goroutine comunes, incluyendo: modelo maestro de trabajo, un modelo productor-consumidor, y así sucesivamente.

referencia

  • "La concurrencia de Go"
  • https://blog.golang.org/pipelines
  • https://blog.golang.org/context
  • https://www.openmymind.net/Leaking-Goroutines/
  • https://blog.minio.io/debugging-go-routine-leaks-a1220142d32c
Publicados 158 artículos originales · ganado elogios 119 · vistas 810 000 +

Supongo que te gusta

Origin blog.csdn.net/u013474436/article/details/104630559
Recomendado
Clasificación