GO Series Idioma - de goroutine de Canal

A linguagem núcleo apresenta golang

goroutine

introdução básica

Processos e threads apresentação

  1. Processo é um processo de execução do programa no sistema operacional, o sistema é a unidade básica de alocação de recursos e agendamento

  2. Um fio é um processo de execução de exemplo, é a menor unidade de execução do programa, é menor do que o processo da unidade básica pode operar de forma independente

  3. Um processo pode criar e destruir vários segmentos, vários segmentos no mesmo processo pode ser executado simultaneamente

  4. Um programa tem pelo menos um processo, um processo tem pelo menos um fio

Mostrando a relação entre os programas, processos e threads

Concurrent e Paralela

  1. programas multithread em execução em um único núcleo, é complicada por

  2. programa de vários segmentos é executado em uma de múltiplos núcleos, é paralelo

    simultaneamente: Como em um processador central, tais como 10 segmentos, cada segmento de execução 10 milissegundos (operação de sondagem), a partir de um ponto de vista humano, se este segmento 10 no prazo, mas do ponto de vista microscópico, em um determinado ponto de vista, existe apenas um segmento de execução, o que é complicado pela

Paralela: porque na pluralidade de CPU (como CPU 10 tem), tal como 10 segmentos, cada segmento de execução 10 ms (cada uma realizada em um processador diferente), a partir de uma perspectiva humano, estas linhas 10 são correr, mas do ponto de vista microscópico, olhar em um determinado ponto no tempo, também existem 10 threads em execução, que é paralela

Go and Go coroutine thread principal

Vá o segmento principal (programadores diretamente chamado threads / processos também pode ser entendido): Go em um segmento, pode desempenhar múltiplas co-rotinas, ela pode ser entendida: coroutine é thread leve] [otimização do compilador para fazer

Ir recursos coroutine

1) tem um espaço de pilha separada

2) espaço programa de compartilhamento de pilha

3) pelo controlo de programação de utilizador

4) co-rotina é thread leve

Introdução

Observações

Escreva um programa que executa as seguintes funções:

1) o segmento principal (que pode ser entendido como um processo), abrir um goroutines, a saída de cada segundo co-rotina "Olá, mundo"

2) o segmento principal é também a saída a cada segundo "Olá, mundo", a saída 10, para sair do programa

3) exige que o segmento principal e, simultaneamente, realiza goroutine

E a principal execução de thread fluxograma mostrado coroutine

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
//......

resumo

  1. fio física é um segmento principal, diretamente na CPU. Heavyweight, ele está consumindo recursos da CPU

  2. Coroutine Chengkai Qi da linha principal é o segmento leve, estado lógico. O consumo de recursos é relativamente pequeno

  3. Go mecanismos de co-rotina são características importantes que podem ser facilmente transformado dezenas de milhares de co-rotinas. Simultaneidade outras linguagens de programação são geralmente thread-base, abertos muitos segmentos, consumindo recursos grande vantagem aqui destaca Go em um concorrente

modelo de programação goroutine

  1. M: thread principal do sistema operacional (thread físico)

  2. P: contexto de execução coroutine necessário

  3. G: coroutine

MPG estado modo -1

  1. Há três programa atual M, se M três corridas em uma CPU, é complicado, quando rodando em uma CPU diferente é paralela

  2. Ml, M2, M3 está executando uma fila coroutine G, M1 tem três, M2 fila coroutine tem três, M3 fila coroutine tem dois

  3. Pode ser visto a partir da figura: Go coroutine é thread leve, estado lógico, de dezenas de milhares podem ser facilmente Go coroutine

  4. Outros procedimentos c / java multithreading, modo muitas vezes kernel, a comparação dos pesos pesados, milhares de tópicos pode ficar sem cpu

modo de estado de MPG de operação - 2

  1. Ver em duas partes

  2. A situação original é o thread principal está executando MO Go coroutine, e outros três na coroutine fila de espera

  3. Se Go obstrução coroutine, tais como leitura de documentos ou bancos de dados, etc.

  4. Em seguida, o segmento principal é criado M1 (M1 pode ser removido do pool de threads existentes), e vai esperar três coroutine ligado para a próxima M1 começou, Go no segmento principal M0 continua a ser o io arquivo executável leitura e escrita

  5. Tal modo de MPG agendamento, você pode deixar Go executado, e não deixar que outra coroutine foi bloqueado fila, você ainda pode concomitante / execução paralela

  6. Ir Não espere até bloqueada, M0 será colocado na thread principal livre para continuar (retirado do pool de threads existentes), enquanto Go vai acordar

Vai correr para definir o número de CPU

A fim de fazer pleno uso das vantagens do multi-cpu, o programa Go, definir o número de cpu running

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 (gasoduto)

Veja uma demanda

Requisitos: Para calcular agora 1-- fatorial cada número 200, eo número de colocar em cada mapa factorial, o último exibido

Requisitos: Use goroutine

Análise das idéias

1) Use goroutine para completa alta eficiência, mas haverá questões de segurança simultâneos / paralelo

2) Isso levanta a questão de como se comunicar em goroutine diferente

código de área

1) Use goroutine feito (olhada usando goroutine conclusão simultânea do que vai acontecer? Então vá para resolver)

2) Quando você executar um programa, como você sabe se há uma competição por recursos. O método é muito simples, quando você compilar o programa, adicione um parâmetro - corrida 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

Como se comunicar entre goroutine diferente

  1. Mutex variáveis ​​globais

  2. Use canal de tubulação para resolver

Melhor utilização de variáveis ​​globais programa de sincronização de bloqueio

Porque não há nenhum bloqueio sobre variáveis ​​globais m, assim não haverá problemas de contenção de recursos, erros de código, sugerindo que escreve mapa simultâneos

Solução : Adicione um mutex

grande número fatorial, o resultado será fora da faixa, você pode ser mudado factorial soma + = uint64 (i)

código de área melhorada

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

canal Por

  1. sincronização freio dianteiro utilizando variáveis ​​globais para resolver a comunicação goroutine, mas não perfeito

  2. O thread principal é difícil determinar o tempo de espera de todos gorountine concluída, onde 10 segundos é fornecida, apenas estimativas

  3. Se o thread principal para dormir um longo tempo, o tempo de espera será mais longa, se o tempo de espera é curto, pode haver goroutine em condições de trabalho, em seguida, também com a saída do segmento principal e destruição

  4. A sincronização é obtida bloqueando a comunicação variável global, não é propício para uma pluralidade de co-rotinas de operações de leitura e gravação de variáveis ​​globais

  5. Acima de todos os tipos de análise que estamos chamando para um novo mecanismo de comunicação - canal

introdução de base de canal

  1. canal é essencialmente uma estrutura de dados - fila

  2. Dados FIFO [FIFO: primeiro int primeiro out]

  3. Thread-safe, multi acesso goroutine, sem bloqueio, que se canal é thread-safe

  4. Há tipo de canal, o canal só pode armazenar uma string string tipo de dados

Definição / canal de comunicação

Chan nome da variável de tipo de dados var

Por exemplo:

var intChan chan int (int intChan para o armazenamento de dados)

var mapChan chan mapa [int] corda (mapChan para armazenar o mapa [int] Tipo de cadeia)

foi perChan chan Pessoa

var perChan2 chan * Pessoa

....

explicação

1) canal é um tipo de referência

2) canal deve ser inicializado para gravação de dados que podem ser usados ​​depois de make

3) há um tipo de tubo, intChan só pode ser escrita inteiro int

Inicializar o gasoduto, os dados são gravados para o tubo, os dados lidos a partir do tubo

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

Note-se a utilização do canal

  1. canal só pode especificar o tipo de dados armazenados

  2. Depois que o canal é preenchido com dados, você não pode colocar um

  3. Se os dados são removidos do canal, pode continuar no

  4. Sem a co-rotina uso, se os dados de canal é assumido, em seguida, tomar será relatado bloqueio mortos

apresentação de casos de gravação de canal

  1. Criar um intChan, pode armazenar até três int, dados de apresentação para a memória 3 intChan, em seguida, retire os três 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. Criar um mapChan, pode conter até 10 mapa [cadeia] string de chave-val, escrita apresentação e leitura
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. Criar um catChan, pode realizar-se a variáveis ​​de estrutura 10 do gato, demonstra o uso da escrita e da leitura
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. Criar um catChan2, pode conter até 10 variáveis ​​* CCT, demonstra o uso da escrita e da leitura
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. Criar um allChan, você pode armazenar até 10 variáveis ​​de qualquer tipo de dados, gravação e leitura de uso de demonstração
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. Olhe para a seguinte saída vontade código que
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

e canal de passagem fechado

fecho para canal

Você pode usar o built-in função de canal closed, quando o canal é fechado, você não pode gravar dados para o canal de novo, mas você ainda pode ler os dados a partir do 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

travessia canal

suporte ao canal para - variedade de maneiras para atravessar, atenção ao detalhe dois

  1. Quando travessia, se o canal não está fechada, ele vai impasse erros

  2. Quando travessia, se o canal tiver sido fechada, ele irá normalmente dados transversais, atravessando terminado, ele irá sair travessia

passagem de canal e apresentações dos casos fechados

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

aplicações

Aplicações - conducentes à realização do gasoduto em gravação

Por favor, preencha o goroutine caso e trabalho de canal, os requisitos específicos:

  1. Abrir WriteData um co-rotina, 50 está escrito para o oleoduto inteiro em intChan

  2. Abrir ReadData uma co-rotina, WriteData ler dados gravados a partir do ducto intChan

  3. Nota: operação WriteData e ReadData é o mesmo oleoduto

  4. O thread principal precisa esperar WriteData e ReadData coroutines ter concluído o trabalho de parar [o gasoduto]

análise pensamento

código de área

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

Aplicações - bloqueio


Pergunta: Suponha que escrever fora de ir ler (myChan, exitChan) O que acontecerá então?

Ou seja, apenas ler, mas não escrever myChan myChan, quando os dados armazenados dentro myChan atingiu capacidade myChan, em seguida, continuar em erros de impasse será relatado. Ao mesmo tempo, devido à necessidade de escrever um verdadeiro exitChan, mas exitChan precisa ser escrito após os dados terem sido lidas em um verdadeiro myChan, mas agora não pode ler, isto é, a verdadeira não escreve exitChan, para formar um bloqueio. Suponha que nós vamos aberto ler (myChan, exitChan), temos apenas ler um conjunto de dados que cada um segundo, e em seguida, escreva-se corretamente, isto é, rapidamente escrever, ler muito lentamente, isso vai levar a impasse é? A resposta é não, desde que a leitura, golang haverá um mecanismo que não vai deixar o valor de mais de capacidade de armazenamento myChan myChan.

Aplicações - Obter Prime Number

demanda

Requisitos estatísticas 1-- digitais em 8000, o que é um número primo?

Depois goroutine agora têm o conhecimento e o canal, a ser concluída

Análise das idéias

Os métodos tradicionais: utilizando uma ansa, cada determinação número de loop não é um número primo

moda Concurrent / paralelo: a atribuição de tarefas a uma pluralidade de estatísticas principais (4) para goroutines completos curto espaço de tempo para completar a tarefa

Desenhe análise de idéias

Descrição: Há cinco co-rotina, três tubos. Em que uma digital para coroutine intChan canal para a escrita, os quatro canalização intChan digital adicional para a retirada e determina se o número principal, e, em seguida, escreve o gasoduto primeChan privilegiada, se o último de quatro atrás da qual uma co-rotina depois do trabalho em uma verdadeira gravação para sair do gasoduto, o último ciclo para determinar se o uso dos quatro co-rotinas de ter concluído a tarefa, e sair

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线程退出")
}

Atualize

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

canal detalhes de uso e notas

canal pode ser declarado como de leitura apenas propriedades ou somente gravação

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 só ler-e-escrever apenas as melhores práticas

//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 bloqueio podem ser resolvidos usando dados selecionados obter da tubulação

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 usado recuperar, parecem resolver o processo de Associação pânico, fazendo com que o programa deixe de funcionar

Nota: Se você jogou uma co-rotina, mas esta co-rotina houve pânico, se não capturar esse pânico, ele irá causar o colapso de todo o programa, então podemos usar para captura de recuperar em goroutine em pânico, ser tratada, por isso mesmo se este processo de associação ocorre o problema, mas o segmento principal permanece inalterado, pode 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

exercícios Pipeline

Descrição:

  1. Criar uma estrutura de Pessoa [nome, idade, endereço]

  2. O método de utilização da margem com o exemplo 10 Pessoa criado aleatoriamente e colocadas no canal

  3. canal Ergódica, as informações exibidas em cada instância Pessoa ... terminais

Acho que você gosta

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