Ir: concurrencia

Lo esencial

  1. ¿Qué es un hilo?
    Un hilo es una ruta de ejecución dentro de un proceso.
  2. ¿Por qué multihilo?
    La idea es lograr el paralelismo dividiendo un proceso en múltiples subprocesos. Por ejemplo, en un navegador, varias pestañas pueden ser diferentes hilos.
  3. Proceso e hilo
    La principal diferencia es que los hilos dentro del mismo proceso se ejecutan en un espacio de memoria compartida, mientras que los procesos se ejecutan en espacios de memoria separados.

Goroutine

A goroutinees un hilo ligero gestionado por el tiempo de ejecución de Go.

go f(x, y, z) // starts a new goroutine by running f(x, y, z)

La evaluación de f, x, y, zsucede en el goroutine actual y la ejecución de los fque sucede en el nuevo goroutine.

Las gorutinas se ejecutan en el mismo espacio de direcciones, por lo que el acceso a la memoria compartida debe estar sincronizado. El syncpaquete proporciona primitivas útiles, aunque no las necesitará mucho en Go, ya que hay otras primitivas.

  1. Caso 1
func display(str string) {
    
     
	time.Sleep(2 * time.Second) 
	fmt.Println(str) 
}
func main() {
    
     
	display("NORMAL")
	go display("GOROUTINE")
}

Resultado: NORMAL
2. Caso 2

func display(str string) {
    
     
	time.Sleep(2 * time.Second) 
	fmt.Println(str) 
}
func main() {
    
     
	go display("GOROUTINE")
	display("NORMAL")
}

Resultado: GOROUTINE\nNORMAL
3. Caso 3

func display(str string) {
    
    
	fmt.Println(str) 
}
func main() {
    
     
	go display("GOROUTINE")
	display("NORMAL")
}

Producción: NORMAL

Canales

Los canales son un conducto mecanografiado a través del cual puede enviar y recibir con el operador del canal <-.

ch <- v // send v to channel ch
v := <- ch // receive from ch, and assign value to v

// like map and slice, channel must be created before use
ch := make(chan int)

Ejemplo de dos gorutinas que resuelven una tarea juntas:

func sum(s []int, c chan int) {
    
    
	sum := 0
	for _, v := range s {
    
    
		sum += v
	}
	c <- sum // send sum to c
}

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

	c := make(chan int)
	go sum(s[:len(s)/2], c)
	go sum(s[len(s)/2:], c)
	x, y := <-c, <-c // receive from c

	fmt.Println(x, y, x+y)
}

Canales en búfer

Los canales se pueden almacenar en búfer .
Proporcione la longitud del búfer como segundo argumento makepara inicializar un canal en búfer:

ch := make(chan int, 100)

Envía a un bloque de canal almacenado en búfer solo cuando el búfer está lleno. Recibe bloque cuando el búfer está vacío.

func main() {
    
    
	ch := make(chan int, 2)
	ch <- 1
	ch <- 2
	ch <- 3 // deadlock, all goroutines are asleep
	fmt.Println(<-ch)
	fmt.Println(<-ch)
}

Alcance y cierre

Un remitente puede cerrar un canal:

ch := make(chan int, 2)
ch <- 1
ch <- 2
close(ch)

Los receptores pueden probar si un canal está cerrado:

v, ok := <-ch

El bucle for i := range chrecibe valores del canal chrepetidamente hasta que se cierra.
Enviar en un canal cerrado provocará un panic.
Los canales no son como archivos; normalmente no es necesario cerrarlos. El cierre solo es necesario cuando se le debe decir al receptor que no hay más valores, como para terminar un rangebucle.

func fibonacci(n int, c chan int) {
    
    
	x, y := 0, 1
	for i := 0; i < n; i++ {
    
    
		c <- x
		x, y = y, x+y
	}
	close(c)
}
func main() {
    
    
	c := make(chan int, 10)
	go fibonacci(cap(c), c)
	for i := range c {
    
    
		fmt.Println(i)
	}
}

Seleccione

La selectdeclaración permite que una goroutine espere en múltiples operaciones de comunicación (como c <- x).
A selectbloquea hasta que uno de sus casos pueda ejecutarse, luego ejecuta ese caso. Elige uno al azar si hay varios listos.

func fibonacci(c, quit chan int) {
    
    
	x, y := 0, 1
	for {
    
    
		select {
    
    
		case c <- x:
			x, y = y, x+y
		case <-quit:
			fmt.Println("quit")
			return
		}
	}
}

func main() {
    
    
	c := make(chan int)
	quit := make(chan int)
	go func() {
    
    
		for i := 0; i < 10; i++ {
    
    
			fmt.Println(<-c)
		}
		quit <- 0
	}()
	fibonacci(c, quit)
}

Sintaxis de la función goroutine anónima:

go func() {
    
    
	// function body
}()

El defaultcaso en a selectse ejecuta si no hay otro caso listo.

sincronizar Mutex

// SafeCounter is safe to use concurrently.
type SafeCounter struct {
    
    
	mu sync.Mutex // make sure only one goroutine can access a variable at a time to avoid conflicts
	v  map[string]int
}

// Inc increments the counter for the given key.
func (c *SafeCounter) Inc(key string) {
    
    
	// define a block of code to be executed in mutual exclusion by surrounding it with a call to Lock and Unlock
	c.mu.Lock()
	// Lock so only one goroutine at a time can access the map c.v.
	c.v[key]++
	c.mu.Unlock()
}

// Value returns the current value of the counter for the given key.
func (c *SafeCounter) Value(key string) int {
    
    
	c.mu.Lock()
	// Lock so only one goroutine at a time can access the map c.v.
	defer c.mu.Unlock() // use defer to ensure the mutex will be unlocked
	return c.v[key]
}

func main() {
    
    
	c := SafeCounter{
    
    v: make(map[string]int)}
	for i := 0; i < 1000; i++ {
    
    
		go c.Inc("somekey")
	}
	
	fmt.Println(c.Value("somekey"))
}

Supongo que te gusta

Origin blog.csdn.net/Hita999/article/details/112614638
Recomendado
Clasificación