Ir serie de la lengua - de goroutine al Canal

La lengua de la base cuenta con Golang

directorio

goroutine

introducción básica

Procesos y subprocesos presentación

  1. El proceso es un proceso de ejecución del programa en el sistema operativo, el sistema es la unidad básica de la asignación de recursos y la programación

  2. Un hilo es un proceso de ejecución de ejemplo, es la unidad más pequeña de la ejecución del programa, es más pequeño que el proceso de la unidad básica puede operar independientemente

  3. Un proceso puede crear y destruir múltiples hilos, múltiples hilos en el mismo proceso puede ser ejecutado simultáneamente

  4. Un programa tiene al menos un proceso, un proceso tiene al menos un hilo

Que muestra la relación entre los programas, procesos y subprocesos

Concurrente y Paralela

  1. programas multihilo se ejecutan en un solo núcleo, se complica por

  2. programa de multiproceso se ejecuta en un multi-núcleo, es el paralelo

    simultáneamente: Debido a que en una CPU, tal como 10 hilos de actividad, cada hilo de ejecución 10 milisegundos (operación de sondeo), desde un punto de vista humano, si este hilo 10 en la carrera, pero desde el punto de vista microscópico, en un cierto punto de vista, sólo hay un hilo de ejecución, que se complica por

Paralelas: porque en la pluralidad de CPU (tal como CPU 10 tiene), tal como 10 hilos, cada hilo de ejecución 10 ms (cada uno realizado en una CPU diferente), desde una perspectiva humana, estos hilos 10 son corren, pero desde el punto de vista microscópico, vistazo a un cierto punto en el tiempo, también hay 10 hilos en la ejecución, que es paralela

Go and Go corrutina hilo principal

Ve el hilo principal (programadores directamente llamados hilos / procesos también pueden ser entendidos): Ir en un hilo, puede desempeñar múltiples corrutinas, se puede entender: corrutina es ligero hilo] [optimización del compilador que hacer

Ir características co-rutina

1) tiene un espacio de pila separada

2) espacio de montón programa para compartir

3) por el control de la programación de usuario

4) corrutina es hilo de peso ligero

Primeros pasos

observaciones

Escribir un programa que realiza las siguientes funciones:

1) el hilo principal (que puede ser entendido como un proceso), abra una goroutines, la salida de cada segundo corrutina "hola, mundo"

2) el hilo principal es también de salida cada segundo "hola, mundo", la salida 10, para salir del programa

3) requiere que el hilo principal y lleva a cabo simultáneamente goroutine

Y la ejecución del hilo principal diagrama de flujo mostrado corrutina

import (
   "fmt"
   "strconv"
   "time"
)
//编写一个函数/每隔一秒输出"hello,world"
func test()  {
   for i := 1; i <= 10; i++ {
      fmt.Println("test() hello,world" + strconv.Itoa(i))
      time.Sleep(time.Second)
   }
}
func main()  {
   go test() //开启了一个协程
   for i := 1; i <= 10; i++ {
      fmt.Println("main() hello,world" + strconv.Itoa(i))
      time.Sleep(time.Second)
   }
}
//main() hello,world1        //main主线程和test协程同时执行
//test() hello,world1
//main() hello,world2
//test() hello,world2
//......

resumen

  1. hilo física es un hilo principal, directamente en la CPU. Peso pesado, que está consumiendo recursos de la CPU

  2. Coroutine Chengkai Qi de la línea principal es hilo de peso ligero, estado lógico. El consumo de recursos es relativamente pequeña

  3. mecanismos de co-rutina Go son características importantes que pueden convertirse fácilmente decenas de miles de co-rutinas. Concurrencia otros lenguajes de programación son generalmente basados ​​subprocesos, abiertas demasiadas hebras, consumiendo gran ventaja en este recurso pone de relieve Ir en un concurrente

modelo de programación goroutine

  1. M: hilo principal del sistema operativo (rosca física)

  2. P: contexto de ejecución corrutina requiere

  3. G: corrutina

MPG estado de modo de -1

  1. Hay tres programas M actual, si M tres carreras en una CPU, es complicado, cuando se ejecuta en una CPU diferente es paralela

  2. Ml, M2, M3 está ejecutando una cola corrutina G, M1 tiene tres, cola corrutina M2 tiene tres, cola corrutina M3 tiene dos

  3. Se puede observar en la figura: Ir corrutina es hilo de peso ligero, estado lógico, desde decenas de miles pueden ser fácilmente Ir corrutina

  4. Otros procedimientos c / java multithreading, el modo de frecuencia del núcleo, la comparación de peso pesado, miles de hilos se puede quedar sin CPU

MPG modo de estado de operación - 2

  1. Ver en dos porciones

  2. La situación de partida es el hilo principal está ejecutando MO Ir corrutina, y otros tres en la co-rutina cola de espera

  3. Si la obstrucción corrutina Go, tales como la lectura de documentos o bases de datos, etc.

  4. A continuación, se crea el hilo principal M1 (M1 puede ser eliminado del grupo de subprocesos existente), y esperará tres corrutina ligada a la siguiente M1 comenzar, vaya en el hilo principal sigue siendo el M0 io archivo ejecutable de lectura y escritura

  5. Tal modo de programación MPG, puede dejar ir ejecutado, y no dejar que otros corrutina ha sido bloqueado cola, todavía puede concurrente / ejecución en paralelo

  6. Ir No espere hasta bloqueado, M0 se colocará en el hilo principal libre para continuar (tomado del grupo de subprocesos existente), mientras que Go se despertará

Ve a ejecutar para establecer el número de CPU

Con el fin de hacer un uso completo de las ventajas de multi-CPU, el programa Ve, pon el número de CPU corriendo

import (
   "fmt"
   "runtime"
)

func main()  {
   //获取当前系统cpu的数目
   num := runtime.NumCPU()
   //这里设置num - 1的cpu运行Go程序
   runtime.GOMAXPROCS(num - 1)
   fmt.Println("num = ", num)
}

Go1.8后,默认让程序运行在多核上,可以不用设置
Go1.8前,还是要设置一下,可以更高效的利用cpu

Canal (tubería)

Ver una demanda

Requisitos: Para el cálculo de ahora 1-- factorial cada número 200, y la opción de venta número en cada mapa factorial, el último que aparece

Requisitos: Uso goroutine

Análisis de las ideas

1) El uso de goroutine completa de alta eficiencia, pero no habrá problemas de seguridad concurrentes / paralelos

2) Esto plantea la cuestión de cómo comunicarse en diferentes goroutine

el área de código

1) Uso goroutine hecho (buscar en el uso goroutine realización simultánea de lo que sucederá? A continuación, vaya a resolver)

2) Cuando se ejecuta un programa, hacer la forma de saber si existe una competencia por los recursos. El método es muy simple, cuando se compila el programa, añadir un parámetro - carrera para

diagrama esquemático

import (
	"fmt"
	"time"
)
//思路
//1. 编写一个函数,计算各个数的阶乘,并放入到map中
//2. 启动的协程多个,统计的结果放入到map中
//3. map应该做出一个全局的
var (
	myMap = make(map[int]int,10)
)
//test函数就是计算n!,将这个结果放入到myMap
func test(n int)  {
	res := 1
	for i := 1; i <= n; i++ {
		res *= i
	}
	//这里将res 放入到myMap
	myMap[n] = res // concurrent map writes?
}
func main()  {
	//这里开启多个协程完成这个任务[200个]
	for i := 1; i <= 200; i++ {
		go test(i)
	}
	//休眠10秒钟【第二个问题】
	time.Sleep(time.Second * 10)
	//这里输出结果,遍历这个结果
	for i, v := range myMap {
		fmt.Printf("map[%d] = %d\n", i, v)
	}
}
//fatal error: concurrent map writes
//
//goroutine 55 [running]:
//runtime.throw(0x4d6d6d, 0x15)
//	E:/GO/go/src/runtime/panic.go:774 +0x79 fp=0xc0000eff60 sp=0xc0000eff30 pc=0x42d229
//runtime.mapassign_fast64(0x4b6240, 0xc00005c330, 0x31, 0x0)
//	E:/GO/go/src/runtime/map_fast64.go:101 +0x357 fp=0xc0000effa0 sp=0xc0000eff60 pc=0x410167
//main.test(0x31)
//	E:/gostudent/src/2020-04-06/main.go:21 +0x6b fp=0xc0000effd8 sp=0xc0000effa0 pc=0x49c72b
//runtime.goexit()
//	E:/GO/go/src/runtime/asm_amd64.s:1357 +0x1 fp=0xc0000effe0 sp=0xc0000effd8 pc=0x4556a1
//created by main.main
//	E:/gostudent/src/2020-04-06/main.go:26 +0x5f
//
//goroutine 1 [runnable]:
//time.Sleep(0x2540be400)
//	E:/GO/go/src/runtime/time.go:84 +0x248
//main.main()
//	E:/gostudent/src/2020-04-06/main.go:29 +0x82

¿Cómo comunicar entre diferentes goroutine

  1. Mutex variables globales

  2. Utilizar el canal tubería para resolver

Una mejor utilización de programa de sincronización de bloqueo de variables globales

Debido a que no hay bloqueo en variables globales m, por lo que no habrá problemas de contención de recursos, errores de código, lo que sugiere que las escrituras concurrentes mapa

Solución : Añadir un mutex

gran número factorial, el resultado será fuera de rango, puede ser cambiado factorial suma + = uint64 (i)

el área de código mejorada

package main

import (
	"fmt"
	"sync"
	"time"
)
//思路
//1. 编写一个函数,计算各个数的阶乘,并放入到map中
//2. 启动的协程多个,统计的结果放入到map中
//3. map应该做出一个全局的
var (
	myMap = make(map[uint]uint,10)
	//声明一个全局的互斥锁
	//lock 是一个全局的互斥锁
	//sync 是包:synchornized 同步
	//Mutex :是互斥
	lock sync.Mutex
)
//test函数就是计算n!,将这个结果放入到myMap
func test(n uint)  {
	var res uint = 1
	var i uint = 1
	for ; i <= n; i++ {
		res *= i
	}
	//这里将res 放入到myMap
	//加锁
	lock.Lock()
	myMap[n] = res // concurrent map writes?
	//解锁
	lock.Unlock()
}
func main()  {
	//这里开启多个协程完成这个任务[200个]
	var i uint = 1
	for ; i <= 200; i++ {
		go test(i)
	}
	//休眠10秒钟【第二个问题】
	time.Sleep(time.Second * 10)
	//这里输出结果,遍历这个结果
	lock.Lock()
	for i, v := range myMap {
		fmt.Printf("map[%d] = %d\n", i, v)
	}
	lock.Unlock()
}

需求注意的是:uint64最大到20的阶乘,大整数可以使用math/big 来进行  实例:https://blog.csdn.net/hudmhacker/article/details/90081630

¿Por qué canal

  1. la sincronización de bloqueo frontal por medio de variables globales para resolver la comunicación goroutine, pero no es perfecto

  2. El hilo principal es difícil determinar el tiempo de espera de todos gorountine completado, donde se proporciona 10 segundos, sólo estimaciones

  3. Si el hilo principal para dormir mucho tiempo, el tiempo de espera será más largo, si el tiempo de espera es corto, puede haber goroutine en condiciones de trabajo, a continuación, también con la salida desde el hilo principal y destrucción

  4. La sincronización se consigue mediante el bloqueo de la comunicación variable global, no es propicio para una pluralidad de co-rutinas de operaciones de escritura de variables globales leer y

  5. Por encima de todo tipo de análisis que están llamando a un nuevo mecanismo de comunicación - canal

Introducción básica de canal

  1. canal es esencialmente una estructura de datos - cola

  2. Los datos FIFO [FIFO: primero int primero en salir]

  3. Hilo de seguridad, el acceso de múltiples goroutine, sin bloqueo, que el propio canal es seguro para subprocesos

  4. Hay tipo de canal, el canal sólo puede almacenar una cadena de cadena de tipo de datos

Definición / canal Declaración

Chan nombre de la variable tipo de datos var

Por ejemplo:

var intChan Chan int (intChan para almacenar int datos)

mapa var mapChan Chan [int] string (mapChan para almacenar mapa [int] tipo de cadena)

era perChan Chan persona

var perChan2 Chan * Persona

....

explicación

1) canal es un tipo de referencia

2) del canal debe ser inicializado a escribir los datos que se pueden utilizar después de maquillaje

3) hay un tipo tubo, intChan se puede escribir solamente número entero int

Inicializar la tubería, los datos se escriben a la tubería, los datos leídos de la tubería

package main

import "fmt"

func main()  {
	//演示一下管道的使用
	//1. 创建一个可以存放3个int类型的管道
	var intChan chan int
	intChan = make(chan  int, 3)
	//2. 看看intChan是什么
	fmt.Printf("intChan 的值 = %v intChan本身的地址 = %p\n", intChan, &intChan)
	//3. 向管道写入数据
	intChan <- 10
	num := 211
	intChan <- num
	intChan <- 50
	//intChan <- 99 //当给管道写入数据时,不能超过其容量
	//4. 看看管道的长度和cap(容量)
	fmt.Printf("channel len = %v cap = %v \n", len(intChan), cap(intChan))
	//5. 从管道中读取数据
	var num2 int
	num2 = <- intChan
	fmt.Println("num2 = ", num2)
	fmt.Printf("channel len = %v cap = %v \n", len(intChan), cap(intChan))
	//6. 在没有使用协程的情况下,如果管道数据已经全部取出,再取就会报告deadlock
	num3 := <- intChan
	num4 := <- intChan
	num5 := <- intChan
	fmt.Printf("num3 = %v num4 = %v num5 = %v ", num3, num4, num5)
}
//fatal error: all goroutines are asleep - deadlock!
//intChan 的值 = 0xc000090000 intChan本身的地址 = 0xc00008a018
//channel len = 3 cap = 3 
//num2 =  10
//channel len = 2 cap = 3 
//
//goroutine 1 [chan receive]:
//main.main()
//	E:/gostudent/src/2020-04-06/main.go:28 +0x4d4

Tenga en cuenta el uso del canal

  1. canal sólo puede especificar el tipo de datos almacenados

  2. Después de que el canal está lleno de datos, no se puede poner una

  3. Si los datos se elimina de la canal, puede continuar en el

  4. Sin el uso corrutina, si los datos de canal es asumida, a continuación, tomar esté informado bloqueo de la muerte

presentaciones de casos de canal de escritura

  1. Crear un intChan, puede almacenar hasta tres int, datos de presentación a la memoria 3 intChan, a continuación, quitar los tres int
func main()  {
   var intChan chan int
   intChan = make(chan  int, 3)
   intChan <- 10
   intChan <- 20
   intChan <- 10
   //因为intChan 的容量为3,再存放会报告deadlock
   //intChan <- 50
   num1 := <- intChan
   num2 := <- intChan
   num3 := <- intChan
   //因为intChan 这时已经没有数据了,再取会报告deadlock
   //num4 := <- intChan
   fmt.Printf("num1 = %v num2 = %v num3 = %v", num1, num2, num3)
}
//num1 = 10 num2 = 20 num3 = 10
  1. Crear un mapChan, puede contener hasta un 10 mapa [cadena] cadena de clave-val, la escritura y la lectura de presentación
func main() {
   var mapChan chan map[string]string
   mapChan = make(chan map[string]string, 2)
   m1 := make(map[string]string, 2)
   m1["city1"] = "北京"
   m1["city2"] = "天津"
   m2 := make(map[string]string, 2)
   m2["hero1"] = "宋江"
   m2["hero2"] = "林冲"
   mapChan <- m1
   mapChan <- m2
   num1 := <- mapChan
   num2 := <- mapChan
   fmt.Printf("num1 = %v num2 = %v", num1, num2)
}
//num1 = map[city1:北京 city2:天津] num2 = map[hero1:宋江 hero2:林冲]
  1. Crear un catChan, puede contener hasta 10 variables de estructura gato, demuestra el uso de la escritura y la lectura
type Cat struct{
   Name string
   Age int
}
func main() {
   var catChan chan Cat
   catChan = make(chan Cat, 10)
   cat1 := Cat{Name: "tom", Age: 18,}
   cat2 := Cat{Name: "zise", Age: 18,}
   catChan <- cat1
   catChan <- cat2
   //取出
   cat11 := <- catChan
   cat22 := <- catChan
   fmt.Println(cat11, cat22)
}
//{tom 18} {zise 18}
  1. Crear una catChan2, con capacidad para 10 variables * Cat, demuestra el uso de la escritura y la lectura
type Cat struct{
   Name string
   Age int
}
func main() {
   var catChan chan *Cat
   catChan = make(chan *Cat, 10)
   cat1 := Cat{Name: "tom", Age: 18,}
   cat2 := Cat{Name: "zise", Age: 18,}
   catChan <- &cat1
   catChan <- &cat2
   //取出
   cat11 := <- catChan
   cat22 := <- catChan
   fmt.Println(*cat11, *cat22)
}
//{tom 18} {zise 18}
  1. Crear un allChan, puede contener hasta 10 variables de cualquier tipo de datos, escritura y lectura de demostración de uso
type Cat struct {
   Name string
   Age int
}

func main()  {
   var allChan chan interface{}
   allChan = make(chan interface{}, 10)
   cat1 := Cat{Name: "tom", Age: 18}
   cat2 := Cat{Name: "zise", Age: 18}
   allChan <- cat1
   allChan <- cat2
   allChan <- 10
   allChan <- "jack"
   //取出
   cat11 := <- allChan
   cat22 := <- allChan
   v1 := <- allChan
   v2 := <- allChan
   fmt.Println(cat11, cat22, v1, v2)
}
//{tom 18} {zise 18} 10 jack
  1. Vistazo a la siguiente salida de código de voluntad lo
type Cat struct {
   Name string
   Age int
}

func main()  {
   var allChan chan interface{}
   allChan = make(chan interface{}, 10)
   cat1 := Cat{Name: "tom", Age: 18}
   cat2 := Cat{Name: "zise", Age: 18}
   allChan <- cat1
   allChan <- cat2
   allChan <- 10
   allChan <- "jack"
   //取出
   //cat11 := <- allChan
   //fmt.Println(cat11.Name)
   // # command-line-arguments
   //src\go_code\chapter15\exec03\test03.go:23:19: cat11.Name undefined (type interface {} is interface with no methods)
   newCat := <- allChan //从管道中取出的Cat是什么
   fmt.Printf("newCat = %T newCat = %v \n", newCat, newCat)
   //下面写法是错误的,编译不通过
   //fmt.Printf("newCat.Name = %v", newCat.Name)
   //使用类型断言
   a := newCat.(Cat)
   fmt.Printf("newCat.Name = %v", a.Name)
}
//newCat = main.Cat newCat = {tom 18} 
//newCat.Name = tom

canal y el recorrido cerrado

cierre del canal

Puede utilizar la función incorporada de canal cerrado cierre, cuando el canal está cerrado, no puede escribir datos en el canal de nuevo, pero todavía se puede leer los datos desde el canal

func main()  {
   intChan := make(chan int, 3)
   intChan <- 100
   intChan <- 200
   close(intChan) //close
   //这时不能够再写入数到channel
   //intChan <- 300
   fmt.Println("oko")
   //当管道关闭后,读取数据是可以的
   n1 := <- intChan
   fmt.Println("n1 = ", n1)
}
//oko
//n1 =  100

el recorrido del canal

soporte para canales de - gama de formas de atravesar, la atención al detalle de dos

  1. Cuando el recorrido, si el canal no está cerrado, será un punto muerto errores

  2. Cuando el recorrido, si el canal se ha cerrado, será normalmente de datos transversales, que atraviesa terminado, que se cerrará el recorrido

traversal canal y presentaciones de casos cerrados

func main()  {
     //遍历管道
   intChan2 := make(chan int, 100)
   for i := 0; i < 100; i++ {
      intChan2 <- i *2  //放入100个数据到管道
   }
   //遍历管道不能使用普通的for循环
   //for i := 0; i < len(intChan2); i++ {
   //
   //}
   //1)在遍历时,如果channel没有关闭,则会出现deadlock的错误
   //2)在遍历时,如果channel已经关闭,则会正常遍历数据,遍历完后,就会退出遍历
   close(intChan2)
   for v := range intChan2 {
      fmt.Println("v = ", v)
   }
}

aplicaciones

Aplicaciones - conducentes a la realización de la tubería en la escritura

Por favor complete el goroutine de casos y trabajo del canal, los requisitos específicos:

  1. Abrir WriteData un co-rutina, 50 se escribe en la tubería número entero en intChan

  2. Abrir una ReadData corrutina, WriteData leer los datos escritos desde el conducto intChan

  3. Nota: La operación WriteData y ReadData es la misma tubería

  4. El hilo principal tiene que esperar WriteData y ReadData corrutinas han completado el trabajo de dejar de fumar [la tubería]

El análisis pensamiento

área de código

import (
   "fmt"
   "time"
)
//writeData
func writeData(intChan chan int)  {
   for i := 1; i <= 50; i++ {
      //放入数据
      intChan <- i
      fmt.Println("writeData", i)
      time.Sleep(time.Second)
   }
   close(intChan) //关闭
}
//readData
func readData(intChan chan int, exitChan chan bool)  {
   for {
      v, ok := <- intChan
      if !ok {
         break
      }
      time.Sleep(time.Second)
      fmt.Printf("readData 读到数据 = %v\n", v)
   }
   //readData 读取完数据后,即任务完成
   exitChan <- true
   close(exitChan)
}
func main()  {
   //创建两个管道
   intChan := make(chan int, 50)
   exitChan := make(chan bool, 1)
   go writeData(intChan)
   go readData(intChan, exitChan)
   time.Sleep(time.Second * 10)
   for {
      _,ok := <- exitChan
      if !ok {
         break
      }
   }
}

var (
    myMap = make(map[int]int, 10)
)

func cal(n int) map[int]int {
    res := 1
    for i := 1; i <= n; i++ {
        res *= i
    }
    myMap[n] = res
    return myMap
}

func write(myChan chan map[int]int) {
    for i := 0; i <= 15; i++ {
        myChan <- cal(i)
        fmt.Println("writer data:", cal(i))
    }
    close(myChan)
}

func read(myChan chan map[int]int, exitChan chan bool) {
    for {
        v, ok := <-myChan
        if !ok {
            break
        }
        fmt.Println("read data:", v)
    }
    exitChan <- true
    close(exitChan)
}

func main() {
    var myChan chan map[int]int
    myChan = make(chan map[int]int, 20)
    var exitChan chan bool
    exitChan = make(chan bool, 1)
    go write(myChan)
    go read(myChan, exitChan)
    for {
        _, ok := <-exitChan
        if !ok {
            break
        }
    }
}

Aplicaciones - bloqueantes


Pregunta: Supongamos que escribimos Ir leer (myChan, exitChan) ¿Qué pasará entonces?

Es decir, sólo leer pero no escribir myChan myChan, cuando los datos almacenados en el interior myChan alcanzado su capacidad myChan, y luego continuar en errores de punto muerto será informado. Al mismo tiempo, debido a la necesidad de escribir una verdadera exitChan, pero exitChan necesita ser escrito después de los datos se hayan leído en un verdadero myChan, pero ahora no se puede leer, es decir, la verdadera no escribe exitChan, para formar un bloqueo. Supongamos que van abiertas leer (myChan, exitChan), sólo leemos un conjunto de datos que cada un segundo, y luego que se escriba correctamente, es decir, escribir rápidamente, leer muy lentamente, esto dará lugar a punto muerto ella? La respuesta es no, siempre y cuando la lectura, golang habrá un mecanismo que no permitirá que el valor de más de capacidad de almacenamiento myChan myChan.

Aplicaciones - Obtener los números primos

demanda

Requisitos estadísticas 1-- digitales en 8000, que es un número primo?

Después goroutine ahora tienen el conocimiento y el canal, que se completará

Análisis de las ideas

Los métodos tradicionales: el uso de un bucle, cada determinación del número de lazo no es un número primo

Concurrente / forma paralela: la asignación de tareas a una pluralidad de estadísticas principales (4) a goroutines completos corto período de tiempo para completar la tarea

Dibuje análisis de las ideas

Descripción: Hay cinco corrutina, tres tubos. En el que una señal digital a corrutina intChan conducto para la escritura, los cuatro conductos intChan digital adicional para la retirada y determina si el número primo, y luego escribe la tubería primeChan primer, si el último de cuatro detrás de la cual un corrutina después de trabajar en una verdadera escritura para salir de la tubería, el último ciclo para determinar si el uso de los cuatro co-rutinas de haber completado la tarea, y la salida

package main

import (
	"fmt"
	"time"
)
//向intChan放入1 - 8000个数
func putNum(intChan chan int)  {
	for i:= 1; i <= 8000; i++ {
		intChan <- i
	}
	//关闭intChan
	close(intChan)
}
//从intChan取出数据,并判断是否为素数,如果是,就放入到primeChan
func primeNum(intChan chan int, primeChan chan int, exitChan chan bool)  {
	//使用for循环
	//var num int
	var flag bool
	for {
		time.Sleep(time.Millisecond * 10)
		num, ok := <- intChan
		if !ok { //intChan 娶不到..
			break
		}
		flag = true //假设是素数
		//判断num是不是素数
		for i := 2; i < num; i++ {
			if num % i == 0 { //说明该num 不是素数
				flag = false
				break
			}
		}
		if flag {
			//将这个数就放入到primeChan
			primeChan <- num
		}
	}
	fmt.Println("有一个primeNum协程因为取不到数据,退出")
	//这里还不能关闭primeChan
	//向exitChan 写入true
	exitChan <- true
}
func main()  {
	intChan := make(chan int, 200000)
	primeChan := make(chan int, 200000) //放入结果
	//标识退出的管道
	exitChan := make(chan bool, 4) // 4个
	//开启一个协程,向intChan放入1 - 200000个数
	go putNum(intChan)
	//开启四个协程,从intChan取出数据,
	//并判断是否为素数,如果是,就放入到primeChan
	for i := 0; i < 4; i++ {
		go primeNum(intChan, primeChan, exitChan)
	}
	//这里对主线程,进行处理
	go func() {
		for i := 0; i < 4; i++ {
			<- exitChan
		}
		//当从exitChan 取出4个结果
		//就可以关闭prprimeChan
		close(primeChan)
	}()
	//遍历primeChan,把结果取出
	for {
		res, ok := <- primeChan
		if !ok {
			break
		}
		//将结果输出
		fmt.Printf("素数 = %d\n", res)
	}
	fmt.Println("main线程退出")
}

Actualiza

package main

import (
	"fmt"
	"time"
)

func isPrime(n int) bool {
	for i := 2; i <= n; i++ {
		if n%i == 0 {
			return false
		}
	}
	return true
}

//传统方法耗时
func Test() {
	start := time.Now()
	for i := 1; i < 80000; i++ {
		isPrime(i)
	}
	cost := time.Since(start)
	fmt.Printf("传统方法消耗时间为:%s", cost)
}

//向intChan放入1 - 80000个数
func putNum(intChan chan int)  {
	for i:= 1; i <= 80000; i++ {
		intChan <- i
	}
	//关闭intChan
	close(intChan)
}
//从intChan取出数据,并判断是否为素数,如果是,就放入到primeChan
func primeNum(intChan chan int, primeChan chan int, exitChan chan bool)  {
	//使用for循环
	//var num int
	//var flag bool
	for {
		//time.Sleep(time.Millisecond * 10)
		num, ok := <- intChan
		if !ok { //intChan 娶不到..
			break
		}
		//flag = true //假设是素数
		//判断num是不是素数
	//	for i := 2; i < num; i++ {
	//		if num % i == 0 { //说明该num 不是素数
	//			flag = false
	//			break
	//		}
	//	}
	//	if flag {
	//		//将这个数就放入到primeChan
	//		primeChan <- num
	//	}
	//}
		isp := isPrime(num)
		if !isp {
			continue
		} else {
			primeChan <- num
		}
	}
	fmt.Println("有一个primeNum协程因为取不到数据,退出")
	//这里还不能关闭primeChan
	//向exitChan 写入true
	exitChan <- true
}
func main()  {
	intChan := make(chan int, 200000)
	primeChan := make(chan int, 200000) //放入结果
	//标识退出的管道
	exitChan := make(chan bool, 4) // 4个
	//记录当前时间
	start := time.Now()
	//开启一个协程,向intChan放入1 - 200000个数
	go putNum(intChan)
	//开启四个协程,从intChan取出数据,
	//并判断是否为素数,如果是,就放入到primeChan
	for i := 0; i < 4; i++ {
		go primeNum(intChan, primeChan, exitChan)
	}
	//这里对主线程,进行处理
	go func() {
		for i := 0; i < 4; i++ {
			<- exitChan
		}
		//当从exitChan 取出4个结果
		//就可以关闭prprimeChan
		//计算耗时时间
		cost := time.Since(start)
		fmt.Printf("使用协程耗费时间:%s\n", cost)
		close(primeChan)
	}()
	//遍历primeChan,把结果取出
	for {
		_, ok := <- primeChan
		if !ok {
			break
		}
		//将结果输出
		//fmt.Printf("素数 = %d\n", res)
	}
	fmt.Println("main线程退出")
	Test()
}
//有一个primeNum协程因为取不到数据,退出
//有一个primeNum协程因为取不到数据,退出
//有一个primeNum协程因为取不到数据,退出
//有一个primeNum协程因为取不到数据,退出
//使用协程耗费时间:876.6558ms
//main线程退出
//传统方法消耗时间为:3.3300976s

detalles y notas de uso de canales

canal puede ser declarado como propiedades de sólo escritura o sólo lectura

func main()  {
   //管道可以声明为只读或者只写
   //1. 在默认情况下,管道是双向
   //var chan1 chan int //可读可写
   //2. 声明为只写
   var chan2 chan <- int
   chan2 = make(chan int, 3)
   chan2 <- 20
   //num := <- chan2 //error
   fmt.Println("chan2 = ", chan2)
   //3. 声明为只读
   var chan3 <- chan  int
   num2 := <- chan3
   //chan3 <- 30 //err
   fmt.Println("num2", num2)
}

canal de sólo lectura y escritura exclusiva mejores prácticas

//ch chan <- int 这样ch就只能写操作了
func send(ch chan <- int, exitChan chan struct{})  {
   for i := 0; i < 10; i++ {
      ch <- i
   }
   close(ch)
   var a struct{}
   exitChan <- a
}
//ch <- chan int ,这样ch 就只能读操作了
func recv(ch <- chan int, exitChan chan struct{})  {
   for {
      v, ok := <- ch
      if !ok {
         break
      }
      fmt.Println(v)
   }
   var a struct{}
   exitChan <- a
}

func main()  {
   var ch chan  int
   ch = make(chan int, 10)
   exitChan := make(chan struct{}, 2)
   go send(ch, exitChan)
   go recv(ch, exitChan)
   var total = 0
   for _ = range exitChan {
      total ++
      if total == 2 {
         break
      }
   }
   fmt.Println("结束...")
}

problemas de bloqueo pueden ser resueltos utilizando datos ir a buscar selectos de la tubería

import (
   "fmt"
   "time"
)

func main() {
   //使用select可以解决从管道取数据的阻塞问题
   //1. 定义一个管道10个数据int
   intChan := make(chan int, 10)
   for i := 0; i < 10; i++ {
      intChan <- i
   }
   //2. 定义一个管道5个数据string
   stringChan := make(chan string, 5)
   for i := 0; i < 5; i++ {
      stringChan <- "hello" + fmt.Sprintf("%d", i)
   }
   //传统的方法在遍历管道时,如果不关闭会阻塞而导致deadlock
   //问题:在实际开发中,可能不好确定什么时间关闭该管道
   //可以使用select方式解决
   //label:
   for {
      select {
      //注意:这里intChan一直没有关闭,不会一直阻塞而deadlock
      //会自动到下一个case匹配
      case v := <-intChan:
         fmt.Printf("从intChan读取的数据%d\n", v)
         time.Sleep(time.Second)
      case v := <-stringChan:
         fmt.Printf("从stringChan读取的数据%s\n", v)
         time.Sleep(time.Second)
      default:
         fmt.Printf("都取不到了,不玩了,程序员可以加入逻辑\n")
         time.Sleep(time.Second)
         return
         //break label
      }
   }
}

goroutine utiliza recuperarse, parecen resolver el proceso de Asociación de pánico, haciendo que el programa se bloquee

Nota: Si has jugado una co-rutina, pero esto corrutina había pánico, si no capturamos este pánico, causará el colapso de todo el programa, entonces podemos utilizar para la captura de recuperarse en goroutine en pánico, ser entendido, por lo que incluso si esto proceso de Asociación se produce el problema, pero el hilo principal no se ve afectado, puede continuar.

import (
   "fmt"
   "time"
)
//函数
func sayHello()  {
   for i := 0; i < 10; i++ {
      time.Sleep(time.Second)
      fmt.Println("hello,world")
   }
}
//函数
func test()  {
   //这里可以使用defer + recover
   defer func() {
      //捕获test抛出的panic
      if err := recover(); err != nil {
         fmt.Println("test() 发生错误", err)
      }
   }()
   //定义了一个map
   var myMap map[int]string
   myMap[0] = "golang" // error
}
func main()  {
   go sayHello()
   go test()
   for i := 0; i < 10; i++ {
      fmt.Println("main() ok=", i)
      time.Sleep(time.Second)
   }
}
//main() ok= 0
//test() 发生错误 assignment to entry in nil map
//hello,world
//main() ok= 1
//hello,world
//main() ok= 2
//hello,world
//main() ok= 3
//hello,world
//main() ok= 4
//hello,world
//main() ok= 5
//hello,world
//main() ok= 6
//hello,world
//main() ok= 7
//hello,world
//main() ok= 8
//hello,world
//main() ok= 9
//hello,world

ejercicios de tuberías

Descripción:

  1. Crear una estructura persona [Nombre, Edad, Dirección]

  2. El método de uso del rand con la instancia Persona 10 creado al azar y se coloca en el canal

  3. Ergódica canal, la información que se muestra en cada caso el terminal persona ...

Supongo que te gusta

Origin www.cnblogs.com/zisefeizhu/p/12643838.html
Recomendado
Clasificación