Golang controla la concurrencia (sync.WaitGroup y context.Context)

Todos sabemos que la concurrencia es fácil de implementar en golang y solo gose necesita una palabra clave . Pero después de abrir múltiples goruntines, ¿cómo los gestionamos (incluyendo detener y salir de la goroutine, esperar a que la goruntine termine de ejecutarse y continuar dejando que la goruntine se ejecute, etc.). Es posible que nos encontremos con estos en nuestro trabajo diario. Hablemos de cómo administrar goruntine correctamente y con gracia en diferentes escenarios, es decir, controlar la concurrencia.

一 , sync.WaitGroup

Rol: programación de tareas, a la espera de que se completen varias rutinas de gor

Escenario aplicable: cuando muchas gorutinas hacen una cosa en colaboración, porque cada gorutina está haciendo una parte de esto, solo cuando todas las gorutinas están completadas, esto se considera completo, esta es la forma de esperar.

func main() {
    
    
	var wg sync.WaitGroup

	wg.Add(2)
	go func() {
    
    
		time.Sleep(2 * time.Second)
		fmt.Println("1号完成")
		wg.Done()
	}()
	go func() {
    
    
		time.Sleep(2 * time.Second)
		fmt.Println("2号完成")
		wg.Done()
	}()
	wg.Wait()
	fmt.Println("好了,大家都干完了,放工")
}

Esta parte es relativamente simple, pero no tiene mucha descripción. Más información sobre el uso de sync.WaitGroup en lectura y escritura asincrónicas

二 , context.Context

El escenario anterior es salir después de esperar a que se ejecuten varias tareas pequeñas. Considere este escenario a continuación: Hay una tarea continua, que generalmente es permanente y no sale, como una tarea de monitoreo. Pero a veces, debido a necesidades especiales, debe retirarse después de juzgar las condiciones. Debido a que no hay conexión de datos entre esta tarea de monitoreo y otras tareas (canales y similares), sync.WaitGroup no puede salir de ella. Naturalmente, podemos pensar en un medio para notificar la salida de la tarea de monitoreo. Las variables globales pueden servir como este medio, pero debido a la poca seguridad, no se consideran aquí (generalmente, las variables globales no están permitidas en el código).

aviso de chan

func main() {
    
    
	stop := make(chan bool)

	go func() {
    
    
		for {
    
    
			select {
    
    
			case <-stop:
				fmt.Println("监控退出,停止了...")
				return
			default:
				fmt.Println("goroutine监控中...")
				time.Sleep(2 * time.Second)
			}
		}
	}()

	time.Sleep(10 * time.Second)
	fmt.Println("可以了,通知监控停止")
	stop <- true
	//为了检测监控过是否停止,如果没有监控输出,就表示停止了
	time.Sleep(5 * time.Second)
}

Resultado de salida:

goroutine监控中...
goroutine监控中...
goroutine监控中...
goroutine监控中...
goroutine监控中...
可以了,通知监控停止
监控退出,停止了...

Este método sigue siendo relativamente elegante, pero todavía tiene grandes limitaciones. ¿Y si necesitamos controlar un montón de goruntines para acabar? ¿Vamos a hacer mucho chan? ¿O qué pasa si hay más sub-goruntinas derivadas de sub-goruntina? ¿Y si hay infinitas capas de gorutina? Esto es muy complicado, aunque definamos mucho chan, es difícil resolver este problema, porque la cadena de relaciones de goroutine hace que esta escena sea muy complicada.

Cuando conocí a Context
, existían los escenarios mencionados anteriormente. Por ejemplo, para una solicitud de red, cada solicitud debe abrir una goroutine para hacer algo, y estas goroutines pueden abrir otras goroutines. Entonces necesitamos una solución que pueda rastrear gorutinas para lograr el propósito de controlarlas. Este es el Contexto proporcionado por el lenguaje Go. Es muy apropiado llamarlo el contexto, que es el contexto de la goroutine.

Vuelva a escribir el ejemplo anterior usando context.Context:

func main() {
    
    
	ctx, cancel := context.WithCancel(context.Background())
	go func(ctx context.Context) {
    
    
		for {
    
    
			select {
    
    
			case <-ctx.Done():
				fmt.Println("监控退出,停止了...")
				return
			default:
				fmt.Println("goroutine监控中...")
				time.Sleep(2 * time.Second)
			}
		}
	}(ctx)

	time.Sleep(10 * time.Second)
	fmt.Println("可以了,通知监控停止")

	cancel()
	
	//为了检测监控过是否停止,如果没有监控输出,就表示停止了
	time.Sleep(5 * time.Second)
}

El contexto controla múltiples goroutines

func main() {
    
    
	ctx, cancel := context.WithCancel(context.Background())
	go watch(ctx, "【监控1】")
	go watch(ctx, "【监控2】")
	go watch(ctx, "【监控3】")

	time.Sleep(10 * time.Second)
	fmt.Println("可以了,通知监控停止")
	cancel()
	//为了检测监控过是否停止,如果没有监控输出,就表示停止了
	time.Sleep(5 * time.Second)
}

func watch(ctx context.Context, name string) {
    
    
	for {
    
    
		select {
    
    
		case <-ctx.Done():
			fmt.Println(name, "监控退出,停止了...")
			return
		default:
			fmt.Println(name, "goroutine监控中...")
			time.Sleep(2 * time.Second)
		}
	}
}

Supongo que te gusta

Origin blog.csdn.net/csdniter/article/details/109671025
Recomendado
Clasificación