[Golang] ir goroutine concurrente

Ir concurrente

explicación

Algunas personas van lenguaje C Comparadas del siglo 21, el primer movimiento se debe a que el diseño es relativamente simple, la segunda en el siglo 21, lo más importante es que la programación concurrente, y pasar de nivel para apoyar el lenguaje concurrente.

Al mismo tiempo, la gestión de memoria de programa concurrente es muy compleja, y proporciona un mecanismo para la recolección de basura en cualquier lugar.

Ir al lenguaje de programación concurrente y una función de API se basa en la parte superior se comunica proceso secuencial modelo CSP (comunicación de procesos secuenciales). Esto significa que los bloqueos explícitos pueden evitarse, porque Ir a enviar y recibir datos a través de un canal relativamente segura para lograr la sincronización, lo que simplifica enormemente los programas concurrentes escritura.

programa de idiomas Ir a implementar el uso simultáneo de dos herramientas principales. goroutine y el canal.

goroutine

Ir goroutine es el núcleo de la programación concurrente, en el análisis final, corrutina goroutine es más pequeño que el hilo, ir dentro de la lengua para ayudar a lograr la memoria compartida entre goroutine. ejecución goroutine requiere mínimo de memoria de pila (probablemente 4-5kb), por supuesto, será escalable de acuerdo con los datos correspondientes. Precisamente por esto, miles de tareas concurrentes se pueden ejecutar simultáneamente. goroutine el hilo más fácil de usar, más eficiente y más ligero.

En circunstancias normales, un equipo de funcionamiento generales decenas de hilos en un poco demasiado grande una carga, pero la misma máquina, pero se pueden hacer fácilmente cientos de recursos goroutine para competir.

uso

¿Quieres crear un goroutine, basta con añadir un ir palabra clave en frente de las funciones ordinarias, puede crear unidad de ejecución concurrente.

En la programación concurrente, un proceso que normalmente queremos cortar en pedazos, y luego dejar que cada goroutine cada uno responsable de un trabajo, cuando empieza un programa, las principales carreras de función en una goroutine separada, que llaman la goroutine principal. El nuevo goroutine se crea con la sentencia marcha. lenguaje de diseño concurrente y vez, por lo que podemos lograr fácilmente este objetivo.

package main

import (
    "fmt"
    "time"
)

func main() {
    // 创建一个goroutine
    go func() {
        for i:=0;i<10;i++ {
            fmt.Printf("子goroutine输出%d\n",i)
            time.Sleep(time.Second * 2)
        }
    }()

    for i:=0;i<10;i++ {
        fmt.Printf("主main goroutine输出%c\n",i+'a')
        time.Sleep(time.Second * 2)
    }
}

salida:

主main goroutine输出a
子goroutine输出0
子goroutine输出1
主main goroutine输出b
子goroutine输出2
主main goroutine输出c
子goroutine输出3
主main goroutine输出d
子goroutine输出4
主main goroutine输出e
子goroutine输出5
主main goroutine输出f
子goroutine输出6
主main goroutine输出g
子goroutine输出7
主main goroutine输出h
子goroutine输出8
主main goroutine输出i
主main goroutine输出j
子goroutine输出9

Process finished with exit code 0

propiedad

Cuando el principal goroutine dejar de fumar, otra goroutine se cerrará.

package main

import (
"fmt"
"time"
)

func newTask() {
    i := 0
    for {
        i++
        fmt.Printf("new goroutine: i = %d\n", i)
        time.Sleep(1 * time.Second) //延时1s
    }
}

func main() {
    //创建一个 goroutine,启动另外一个任务
    go newTask()

    fmt.Println("main goroutine exit")
}

salida:

Ejemplos

Aquí vamos a crear un ejemplo, goroutine principal calculará el valor de los primeros 45 elementos de serie de Fibonacci. Debido al uso ineficiente de la función de cálculo recursivo, se ejecutará un tiempo bastante largo, durante el cual queremos que los usuarios ven un logotipo visible para indicar que el programa está todavía en funcionamiento normal, por lo que hacer un icono animado poco:

func main() {
    go spinner(100 * time.Millisecond)
    const n = 45
    fibN := fib(n) // slow
    fmt.Printf("\rFibonacci(%d) = %d\n", n, fibN)
}

func spinner(delay time.Duration) {
    for {
        for _, r := range `-\|/` {
            fmt.Printf("\r%c", r)
            time.Sleep(delay)
        }
    }
}

func fib(x int) int {
    if x < 2 {
        return x
    }
    return fib(x-1) + fib(x-2)
}

Después de la animación de la pantalla durante unos segundos, la llamada fib (45) con éxito devoluciones, e imprimir los resultados:

Fibonacci(45) = 1134903170

A continuación, los principales devuelve la función. Las principales declaraciones de función, todos goroutine serán interrumpidos directamente, el programa se cierra.

Aquí nos fijamos en un ejemplo de servidor de reloj, una orden de ejecución, será cada segundo de la hora actual se escribe en el cliente:

package main

import (
    "io"
    "log"
    "net"
    "time"
)

func main() {
    listener, err := net.Listen("tcp", "localhost:8000")
    if err != nil {
        log.Fatal(err)
    }

    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Print(err) // e.g., connection aborted
            continue
        }
        handleConn(conn) // handle one connection at a time
    }
}

func handleConn(c net.Conn) {
    defer c.Close()
    for {
        _, err := io.WriteString(c, time.Now().Format("15:04:05\n"))
        if err != nil {
            return // e.g., client disconnected
        }
        time.Sleep(1 * time.Second)
    }
}

En el código anterior, se escucha a través del objeto detector conectado al puerto de red, el código anterior es por localhost escuchando el protocolo TCP: puerto 8000, mientras Aceptar bloqueará el programa hasta que se crea una nueva conexión, y luego se vuelve una red objeto .Conn representa una conexión.

handleConn función maneja unas conexiones de clientes completos. Por muerto en un bucle, con time.Now () para obtener la hora actual, y luego por escrito al cliente. Desde net.Conn se dio cuenta de interfaz de io.Writer, podemos escribir directamente al contenido. Este bucle infinito se ejecutará hasta que el error de escritura. La razón más probable es que el cliente tome la iniciativa de desconexión. función handleConn llama el lado del servidor se conecta con el cierre aplazar este caso, y luego vuelve a la función principal, sigue esperando una próxima petición de conexión.

Después de la terminación del código de operación anterior para ser ejecutado por un nc conexión de red (netcat).

$ go build gopl.io/ch8/clock1
$ ./clock1 &
$ nc localhost 8000
13:58:54
13:58:55
13:58:56
13:58:57
^C

A través del código anterior hemos completado los requisitos anteriores, pero esta vez sólo para satisfacer la conexión de una persona, si dos o más personas se conecten, entonces sólo puede ser la primera persona sale de la conexión con la gente detrás las conexiones entrantes.

Si usted quiere alcanzar más de este tiempo conectado mientras el tiempo de impresión, podemos cambiar nuestro código de un poco en la demanda que se puede lograr usando goroutine, de la siguiente manera:

for {
    conn, err := listener.Accept()
    if err != nil {
        log.Print(err) // e.g., connection aborted
        continue
    }
    go handleConn(conn) // handle connections concurrently
}

Por el código anterior, podemos lograr los requisitos anteriores, cada conexión de cliente, se creará un goroutine encuentra fuera de las goroutine principal, por lo que todo el mundo al mismo tiempo el efecto del tiempo de impresión se puede lograr.

A continuación nos fijamos en la demanda de en profundidad, en la aplicación del código, y permite la adición de zona horaria puerto, de modo que cuando se conecta un usuario, se puede obtener el tiempo a la zona horaria actual. Código se implementa de la siguiente manera:

package main

import (
    "flag"
    "io"
    "log"
    "net"
    "time"
)

var port = flag.String("port","8000","请输入端口号")

func main() {
    flag.Parse()
    listener,err := net.Listen("tcp","localhost:"+*port)
    if err != nil {
        log.Fatal(err)
    }


    for {
        conn,err := listener.Accept()
        if err != nil {
            log.Print(err)
            continue
        }

        go handleConnect(conn)

    }


}

func handleConnect(c net.Conn) {

    // 关闭
    defer c.Close()

    for {
        _,err := io.WriteString(c,time.Now().Format("15:04:05\n"))
        if err != nil {
            return
        }
        time.Sleep(1 * time.Second)
    }

}

la ejecución de código:

paquete de tiempo de ejecución

Gosched

Cuando runtime.Gosched () para que el intervalo de tiempo de CPU, por lo que el actual goroutine permisos de ejecución, schedule otras tareas en espera de ejecución y obtener en la pieza redonda próxima vez de tiempo de CPU para recuperarse de la ubicación de la ejecución de la transferencia de la CPU .

Un poco como correr una carrera de relevos, Un corrió una carrera en el código runtime.Gosched () toma el relevo a B, A para recuperarse, B continúe funcionando.

El siguiente código no se utiliza runtime.Gosched código ():

package main

import (
    "fmt"
)

func main() {
    go func(str string) {
        for _,val := range str {
            fmt.Printf("子goroutine:%c\n",val)
        }
    }("hello")

    for _,val := range "world" {
        fmt.Printf("主goroutine:%c\n",val)
    }
}

salida:

子goroutine:h
子goroutine:e
子goroutine:l
子goroutine:l
子goroutine:o
主goroutine:w
主goroutine:o
主goroutine:r
主goroutine:l
主goroutine:d

Process finished with exit code 0

Deje que permiten el uso de la CPU goroutine runtime.Gosched porción de tiempo principal. De la siguiente manera:

package main

import (
    "fmt"
    "runtime"
)

func main() {
    go func(str string) {
        for _,val := range str {
            fmt.Printf("子goroutine:%c\n",val)
        }
    }("hello")

    for _,val := range "world" {
        runtime.Gosched() // 让出cpu时间片
        fmt.Printf("主goroutine:%c\n",val)
    }
}

Cuando deje fuera del intervalo de tiempo, la salida resultante es:

子goroutine:h
子goroutine:e
子goroutine:l
子goroutine:l
子goroutine:o
主goroutine:w
主goroutine:o
主goroutine:r
主goroutine:l
主goroutine:d

Process finished with exit code 0

Cuando dejó escapar CPU segmento de tiempo, se llevará a cabo el primer sub-goroutine, a continuación, la ejecución de la goroutine principal.

Goexit

Runtime.Goexit llamada () terminará inmediatamente el goroutine actual Sí, garantiza el planificador que todos Defer registrado para llamadas de retardo se lleva a cabo.

package main

import (
"fmt"
"runtime"
)

func main() {
    go func() {
        defer fmt.Println("A.defer")

        func() {
            defer fmt.Println("B.defer")
            runtime.Goexit() // 终止当前 goroutine, import "runtime"
            fmt.Println("B") // 不会执行
        }()

        fmt.Println("A") // 不会执行
    }()     //不要忘记()

    //死循环,目的不让主goroutine结束
    for {
    }
}

Resultado del programa:

GOMAXPROCS

Runtime.GOMAXPROCS call () se utiliza para establecer el número máximo de núcleos de CPU puede ser computación en paralelo, y devuelve el valor anterior.

package main

import (
    "fmt"
    "runtime"
)

func main () {
    // 设置cpu核数
    // 括号内的参数表示设置的最大cpu核数
    // 返回值表示上一次设置的cpu核数
    n1 := runtime.GOMAXPROCS(1) // 第一次调用返回默认的核数 4 
    fmt.Println(n1)

    n2 := runtime.GOMAXPROCS(2) // 第二次调用返回上一次设置的核数 1 
    fmt.Println(n2)
    
}

Los resultados son los siguientes:

4
1

Process finished with exit code 0

Supongo que te gusta

Origin www.cnblogs.com/liujunhang/p/12535987.html
Recomendado
Clasificación