zoukankan      html  css  js  c++  java
  • 【原创】go语言学习(二十)并发编程

    目录

    • 并发和并行
    • Goroutine初探
    • Goroutine实战
    • Goroutine原理浅析
    • Channel介绍
    • Waitgroup介绍
    • Workerpool的实现

    并发和并行

    1、概念
    A. 并发:同一时间段内执行多个操作。
    B. 并行:同一时刻执行多个操作。

    Goroutine初探

    1、多线程

    A. 线程是由操作系统进行管理,也就是处于内核态。
    B. 线程之间进行切换,需要发生用户态到内核态的切换。
    C. 当系统中运行大量线程,系统会变的非常慢。
    D. 用户态的线程,支持大量线程创建。也叫协程或goroutine。

    2、 创建goroutine

    package main
    import (
        "fmt"
    )
    func hello() {
        fmt.Println("Hello world goroutine")
    }
    func main() {
        go hello()
        fmt.Println("main function")
    }
    

      

    3、修复代码:主进程存在,goroutine才能执行。

    package main
    import (
        "fmt“
        “time”
    )
    func hello() {
        fmt.Println("Hello world goroutine")
    }
    func main() {
        go hello()
        time.Sleep(1*time.Second)
        fmt.Println("main function")
    }
    

      

    Goroutine实战

    1、 启动多个goroutine

    package main
    import (
        "fmt"
        "time"
    )
    func numbers() {
        for i := 1; i <= 5; i++ {
            time.Sleep(250 * time.Millisecond)
            fmt.Printf("%d ", i)
        }
    }
    func alphabets() {
        for i := 'a'; i <= 'e'; i++ {
            time.Sleep(400 * time.Millisecond)
            fmt.Printf("%c ", i)
        }
    }
    func main() {
        go numbers()
        go alphabets()
        time.Sleep(3000 * time.Millisecond)
        fmt.Println("main terminated")
    }
    

      

    2、程序分析

     3、 多核控制

    A. 通过runtime包进行多核设置
    B. GOMAXPROCS设置当前程序运行时占用的cpu核数
    C. NumCPU获取当前系统的cpu核数

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func hello(i int) {
    	fmt.Println("hello goroutine", i)
    }
    
    func main() {
    	//runtime.GOMAXPROCS(1)
    	//fmt.Println(runtime.NumCPU())
    
    	//单线程
    	//hello()
    	//fmt.Println("mainthread terminate")
    
    	// go 多线程
    	//go hello()
    	//fmt.Println("mainthread terminate")
    	//time.Sleep(time.Second)
    
    	for i := 0; i < 10; i++ {
    		go hello(i)
    	}
    	time.Sleep(time.Second)
    }
    

      

    Goroutine原理浅析

    1、模型抽象

    A. 操作系统线程: M
    B. 用户态线程(goroutine): G
    C. 上下文对象:P

    2、goroutine调度

    3、系统调用怎么处理

    Channel介绍

    1、channel介绍

    A. 本质上就是一个队列,是一个容器
    B. 因此定义的时候,需要只定容器中元素的类型
    C. var 变量名 chan 数据类型

    package main
    import "fmt"
    func main() {
        var a chan int
        if a == nil {
            fmt.Println("channel a is nil, going to define it")
            a = make(chan int)
            fmt.Printf("Type of a is %T", a)
        }
    }
    

      

    2、元素入队和出队

    A. 入队操作,a <- 100
    B. 出队操作:data := <- a

    package channel
    
    import "fmt"
    
    // 管道
    
    func main() {
    	var c chan int
    	fmt.Printf("c=%v", c)
    
    	// 初始化通道int型,10个元素
    	c = make(chan int, 10)
    	fmt.Printf("c=%v", c)
    
    	// 插入数据
    	c <- 100
    	/*
    		// 读取数据
    		data := <- c
    		fmt.Printf("data:%v
    ", data)
    	*/
    
    	// 丢弃元素
    	<-c
    }
    

      

    3、阻塞chan

    package main
    import "fmt"
    func main() {
        var a chan int
        if a == nil {
            fmt.Println("channel a is nil, going to define it")
            a = make(chan int)
            a <- 10
            fmt.Printf("Type of a is %T", a)
        }
    }
    

      

    4、使用chan来进行goroutine同步

    package main
    import (
    "fmt"
    )
    func hello(done chan bool) {
        fmt.Println("Hello world goroutine")
        done <- true
    }
    func main() {
        done := make(chan bool)
        go hello(done)
        <-done
        fmt.Println("main function")
    }
    

      

    5、使用chan来进行goroutine同步

    package main
    import (
        "fmt"
        "time"
    )
    func hello(done chan bool) {
        fmt.Println("hello go routine is going to sleep")
        time.Sleep(4 * time.Second)
        fmt.Println("hello go routine awake and going to write to done")
        done <- true
    }
    func main() {
        done := make(chan bool)
        fmt.Println("Main going to call hello go goroutine")
        go hello(done)
        <-done
        fmt.Println("Main received data")
    }
    

      

    6、单向chan

    package main
    import "fmt"
    func sendData(sendch chan<- int) {
        sendch <- 10
    }
    func readData(sendch <-chan int) {
        sendch <- 10
    }
    func main() {
        chnl := make(chan int)
        go sendData(chnl)
        readData(chn1)
    }
    

      

    7、chan关闭

    package main
    import (
        "fmt"
    )
    func producer(chnl chan int) {
        for i := 0; i < 10; i++ {
            chnl <- i
        }
        close(chnl)
    }
    func main() {
        ch := make(chan int)
        go producer(ch)
        for {
            v, ok := <-ch
            if ok == false {
                break
            }
            fmt.Println("Received ", v, ok)
        }
    }    
    

      

    8、 for range操作

    package main
    import (
        "fmt"
    )
    func producer(chnl chan int) {
        for i := 0; i < 10; i++ {
            chnl <- i
        }
        close(chnl)
    }
    func main() {
        ch := make(chan int)
        go producer(ch)
        for v := range ch {
            fmt.Println("Received ",v)
        }
    }
    

      

    9、 带缓冲区的chanel

    A. Ch := make(chan type, capacity)

    package main
    import (
        "fmt"
    )
    func main() {
        ch := make(chan string, 2)
        ch <- “hello"
        ch <- “world"
        fmt.Println(<- ch)
        fmt.Println(<- ch)
    }
    

      

    package main
    import (
        "fmt"
        "time"
    )
    func write(ch chan int) {
        for i := 0; i < 5; i++ {
            ch <- i
            fmt.Println("successfully wrote", i, "to ch")
        }
        close(ch)
    }
    func main() {
        ch := make(chan int, 2)
        go write(ch)
        time.Sleep(2 * time.Second)
        for v := range ch {
            fmt.Println("read value", v,"from ch")
            time.Sleep(2 * time.Second)
        }
    }
    

      

    10、channel的长度和容量

    A. Ch := make(chan type, capacity)

    package main
    import (
        "fmt"
    )
    func main() {
        ch := make(chan string, 3)
        ch <- "naveen"
        ch <- "paul"
        fmt.Println("capacity is", cap(ch))
        fmt.Println("length is", len(ch))
        fmt.Println("read value", <-ch)
        fmt.Println("new length is", len(ch))
    }
    

      

    Waitgroup介绍

    1、 如何等待一组goroutine结束?

    A. 方法一,使用不带缓冲区的channel实现

    package main
    import (
        "fmt"
        "time"
    )
    func process(i int, ch chan bool) {
        fmt.Println("started Goroutine ", i)
        time.Sleep(2 * time.Second)
        fmt.Printf("Goroutine %d ended
    ", i)
        ch <- true
    }
    func main() {
        no := 3
        exitChan := make(chan bool, no)
        for i := 0; i < no; i++ {
            go process(i, exitChan)
        }
        for i := 0; I < no;i++{
            <-exitChan
        }
        fmt.Println("All go routines finished executing")
    }

    B. 方法二,使用sync. WaitGroup实现

    package main
    import (
        "fmt"
        "sync"
        "time"
    )
    func process(i int, wg *sync.WaitGroup) {
        fmt.Println("started Goroutine ", i)
        time.Sleep(2 * time.Second)
        fmt.Printf("Goroutine %d ended
    ", i)
            wg.Done()
        }
    func main() {
        no := 3
        var wg sync.WaitGroup
        for i := 0; i < no; i++ {
            wg.Add(1)
            go process(i, &wg)
        }
        wg.Wait()
        fmt.Println("All go routines finished executing")
    }
    

      

    Workerpool的实现

    1、worker池的实现

    A. 生产者、消费者模型,简单有效
    B. 控制goroutine的数量,防止goroutine泄露和暴涨
    C. 基于goroutine和chan,构建workerpool非常简单

    package mail
    
    import (
    	"fmt"
    	"math/rand"
    )
    
    // worker生产者消费者模型
    type Job struct {
    	Number int
    	Id int
    }
    
    type Result struct {
    	job *Job
    	sum int
    }
    
    func calc(job *Job, result chan *Result){
    	var sum int
    	number := job.Number
    	for number != 0 {
    		tmp := number % 10
    		sum += tmp
    		number /= 10
    	}
    
    	r := &Result{
    		job: job,
    		sum: sum,
    
    	}
    
    	result <- r
    }
    
    func Worker(){
    	for job:= range jobChan {
    		calc(job, resultChan)
    	}
    }
    
    func startWorkerPool(num int, JobChan chan *Job, resultChan *Result){
    	for i := 0; i < num; i++ {
    		go Worker(JobChan, resultChan)
    	}
    }
    
    func printResult(resultChan chan*Result) {
    	for result := range resultChan {
    		fmt.Printf("job id:%v number:%v result:%d
    ",result.job.Id, result.job.Number, result.sum)
    	}
    }
    
    func main(){
    	jobChan := make(chan *Job, 1000)
    	resultChan := make(chan *Result, 1000)
    
    	startWorkerPool(128, jobChan, resultChan)
    
    	for i := 0; i < 128; i ++ {
    		go calc()
    	}
    
    	go printResult(resultChan)
    	var id int
    	for {
    		id++
    		number := rand.Int()
    		job := &Job {
    			Id: id,
    			Number: number,
    		}
    
    		jobChan <- job
    	}
    }
    

      

    2、项目需求分析

    A. 计算一个数字的各个位数之和,比如123,和等于1+2+3=6
    B. 需要计算的数字使用随机算法生成

    3、方案介绍

    A. 任务抽象成一个个job
    B. 使用job队列和result队列
    C. 开一组goroutine进行实际任务计算,并把结果放回result队列

  • 相关阅读:
    如何入门深度学习?
    java opencv使用相关
    python操作Excel读写--使用xlrd
    从声学模型算法总结 2016 年语音识别的重大进步丨硬创公开课
    sift 与 surf 算法
    BP神经网络原理详解
    Nature重磅:Hinton、LeCun、Bengio三巨头权威科普深度学习
    浅谈流形学习(转)
    远离神经网络这个黑盒,人工智能不止这一条路可走
    对比深度学习十大框架:TensorFlow 并非最好?
  • 原文地址:https://www.cnblogs.com/wangshuyang/p/11820284.html
Copyright © 2011-2022 走看看