Go并发特点

  • goroutine只是由官方实现的超级"线程池"而已,每个实例4-5kb的栈内存占用和用于实现机制而大幅减少的创建和销毁开销。

  • 并发不是并行(多CPU): Concurrency Is Not Parallelism

  • 并发主要由切换时间片来实现"同时"运行,并行则是直接利用多核实现多线程的运行,但Go可以设置使用核数,以发挥多核计算机的能力。

    • 通过go关键字实现多线程
    package main
    import (
    "fmt"
    "time"
    ) func Go() {
    fmt.Println("1234...") } func main() {
    go Go() //go关键字构成多线程
    time.Sleep(2 * time.Second) //主程序睡眠2s
    }
  • Goroutine 奉行通过通信来共享内存,而不是共享内存来通信

Channel

  • Channel是goroutine沟通的桥梁,大都是阻塞同步的

  • 通过make创建,close关闭(当程序简单时,回自动关闭)

    package main
    import "fmt" func main() { //主程序
    c := make(chan bool) //初始化一个chan类型
    go func() { //子程序
    fmt.Println("123...") //执行主程序
    c <- true //通过<-存入bool类型到chan中
    }()
    fmt.Println(1) //程序执行步骤:1st
    read_chan := <-c //<-c 从chan中读取bool,程序执行步骤:2nd
    fmt.Println(read_chan) //程序执行步骤:3rd
    } /*output
    1st 1
    2nd 123...
    3rd true
    */

    注意以上程序的执行顺序(channel无缓存时):先执行读取操作c<-c,因为channel中没有值,所以程序发生阻塞,此时执行chanel写操作,然后再执行读操作。

  • Channel是引用类型

  • 可以使用for range来迭代不断操作channel

    package main
    import "fmt" func main() {
    c := make(chan bool) //初始化一个chan类型
    go func() { //go结合匿名函数,构造并发
    fmt.Println("123...") //执行主程序
    c <- true //通过<-存入bool类型到chan中
    close(c) //关闭通道:必须明确在哪个地方关闭
    }() for v := range c { //for循环chanel
    }
    } /*output
    123...
    true
    */
  • 可以设置单向(读&写)或双向通道--默认是双向通道

  • 可以设置缓存大小(默认为0,阻塞),在未被填充前不会发生阻塞(异步),比如缓存20个,可以同时进行20个读操作或者写操作,注意读的操作先于写的操作

    package main
    import (
    "fmt"
    ) func main() { //主程序
    c := make(chan bool, 1) //初始化一个chan类型,缓存为2
    go func() { //子程序
    fmt.Println("123...") //执行主程序,执行步骤:2
    c <- true //写操作,执行步骤:2
    }()
    fmt.Println(2) //执行步骤:1
    fmt.Println(123, <-c) //读操作,执行步骤:2
    fmt.Println(3) //执行步骤:3
    } /*output
    1 2
    2 123...
    2 123 true
    3 3
    */
  • 并行并发,利用多核CPU,当使用单线程执行的时候,就是同步按部就班的执行程序,使用多核时,且没有设置channel缓存机制时是随机异步发生的。但是这样就会造成一个问题,因为是随机的,所以CPU在选择的时候,某有一个程序并没有执行,会造成程序执行的遗漏。

    package main
    import (
    "fmt"
    "runtime"
    ) func main() { //主程序
    runtime.GOMAXPROCS(runtime.NumCPU()) //获取CPU的核数
    c := make(chan bool) //创建channel
    for i := 0; i < 10; i++ { //启动10次
    go Go(c, i)
    }
    <-c //读取chanel值
    } func Go(c chan bool, index int) {
    a := 1
    for i := 0; i < 1000000; i++ {
    a += i
    }
    fmt.Println(index, a) if index == 9 {
    c <- true //向chanel传入值
    }
    } /*output
    4 499999500001
    9 499999500001
    */
  • 解决异步并发数据丢失:方式1:根据并发执行次数为channel设置同等数量的缓存机制。

    package main
    
    import (
    "fmt"
    "runtime"
    ) func main() { //主程序
    runtime.GOMAXPROCS(runtime.NumCPU()) //获取CPU的核数
    c := make(chan bool, 10) //创建channel
    for i := 0; i < 10; i++ { //启动10次
    go Go(c, i)
    }
    for i := 0; i < 10; i++ { //for 循环为chan循环读取十次
    <-c //读取chanel值
    }
    } func Go(c chan bool, index int) {
    a := 1
    for i := 0; i < 1000000; i++ {
    a += i
    }
    fmt.Println(index, a) c <- true //向chanel传入值 } /*output
    0 499999500001
    9 499999500001
    1 499999500001
    5 499999500001
    6 499999500001
    2 499999500001
    7 499999500001
    3 499999500001
    8 499999500001
    4 499999500001
    */
  • 解决方法2:使用同步 sync 包, sync.WaitGroup{} 创建任务池;sync.add(n)为任务池添加任务个数; sync.Wait()主程序等待(守护进程); sync.Done()子进程全部执行完毕告知主进程,退出程序。

    package main
    import (
    "fmt"
    "runtime"
    "sync"
    ) func main() { //主程序
    runtime.GOMAXPROCS(runtime.NumCPU()) //获取CPU的核数
    wg := sync.WaitGroup{} //调用同步方法,类似一个池子
    wg.Add(10) //增加10个任务数
    for i := 0; i < 10; i++ { //启动10次
    go Go(&wg, i) //wg类型是值拷贝,所以调用时,使用指针传递
    }
    wg.Wait() //等待10个子进程全部执行完毕后,退出
    fmt.Println("Main process exit!")
    } func Go(wg *sync.WaitGroup, index int) {
    a := 1
    for i := 0; i < 1000000; i++ {
    a += i
    }
    fmt.Println(index, a)
    wg.Done() //子程序退出
    }
    /* 输出
    0 499999500001
    9 499999500001
    1 499999500001
    5 499999500001
    6 499999500001
    2 499999500001
    7 499999500001
    3 499999500001
    8 499999500001
    4 499999500001
    Main process exit!
    */

Select

  • 可处理一个或多个channel的发送与接收

    package main
    import (
    "fmt"
    ) func main() { //主程序
    c1, c2 := make(chan int), make(chan string) //创建两个channel
    o := make(chan bool) //创建一个信号通道,用来监控通道c1,c2
    go func() {
    fmt.Println("start...") //go 匿名函数
    for { //这是一个无线循环
    select { //类似switch语句,进行判断
    case v, ok := <-c1: //读取通道c1的值,赋值给变量v
    if !ok { //如果从chan,c1中未读取到值,发生阻塞
    o <- true //在信号通道O中写入true,程序break
    break
    }
    fmt.Println("c1", v) case v, ok := <-c2: //v判断从通道2中读取到的值
    if !ok { //如果读取失败
    o <- true //向信号通道中传递一个信号
    break //程序break
    }
    fmt.Println("c2", v)
    }
    }
    }() c1 <- 1 //通道c1中写入数据
    c2 <- "mm"
    c1 <- 2
    c2 <- "my"
    close(c1) //通道操作完后需关闭通道
    close(c2)
    <-o //最后读取信号通道内容
    }
    /*输出
    start...
    c1 1
    c2 mm
    c1 2
    c2 my
    */

    当有多个channel时,是无法对其中多个channel进行关闭的,只能判断其中一个channel是否关闭,只能进行某个channel进行关闭

  • 同时又多个可用的channel时按随机顺序处理

    package main
    import (
    "fmt"
    ) func main() { //主程序
    c := make(chan int)
    go func() { //go匿名函数
    for v := range c { //循环读取出chan的值
    fmt.Println(v)
    }
    }() for {
    select { //select判断
    case c <- 0:
    case c <- 1:
    }
    }
    } /*输出
    0
    1
    1
    1
    0
    ...
    */
  • 可用空的select来阻塞main函数--应用(GUI程序)

    package main
    import "fmt" func main() { //主程序
    c := make(chan int)
    go func() { //go匿名函数
    for v := range c { //循环读取出chan的值
    fmt.Println(v)
    }
    }() for {
    select { //空select可阻塞main主函数
    }
    }
    }
  • 可设置超时

    package main
    import (
    "fmt"
    "time"
    ) func main() { //主程序
    c := make(chan int)
    select {
    case v := <-c: //channal中没有内容,所以会执行下一个case
    fmt.Println(v)
    case <-time.After(3 * time.Second): //selesct设置超时时间,
    fmt.Println("Timeout!")
    } } /*输出
    Timeout
    */

golang基础--Gocurrency并发的更多相关文章

  1. GoLang基础数据类型--->字符串处理大全

    GoLang基础数据类型--->字符串处理大全 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 欢迎加入:   高级运维工程师之路               59843264 ...

  2. Golang 基础之基础语法梳理 (二)

    大家好,今天将梳理出的 Go语言基础语法内容,分享给大家. 请多多指教,谢谢. 本次<Go语言基础语法内容>共分为三个章节,本文为第二章节 Golang 基础之基础语法梳理 (一) Gol ...

  3. Golang基础教程

    以下使用goland的IDE演示,包含总计的golang基础功能共20个章节 一.go语言结构: 二.go基础语法: 三.变量 四.常量 五.运算符 六.条件语句 七.循环 八.函数 九.变量作用域 ...

  4. golang基础知识之encoding/json package

    golang基础知识之json 简介 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式.可以去json.org 查看json标准的清晰定义.json pack ...

  5. Golang基础之函数

    golang基础之函数 1.为什么需要函数? 有些相同的代码可能出现多次,如果不进行封装,那么多次写入到程序中,会造成程序冗余,并且可读性降低 2.什么是函数 为完成某些特定功能的程序指令集合称为函数 ...

  6. GoLang基础数据类型--->字典(map)详解

    GoLang基础数据类型--->字典(map)详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   可能大家刚刚接触Golang的小伙伴都会跟我一样,这个map是干嘛的,是 ...

  7. GoLang基础数据类型-切片(slice)详解

    GoLang基础数据类型-切片(slice)详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 数组的长度在定义之后无法再次修改:数组是值类型,每次传递都将产生一份副本.显然这种数 ...

  8. GoLang基础数据类型--->数组(array)详解

    GoLang基础数据类型--->数组(array)详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Golang数组简介 数组是Go语言编程中最常用的数据结构之一.顾名 ...

  9. Golang 入门 : 理解并发与并行

    Golang 的语法和运行时直接内置了对并发的支持.Golang 里的并发指的是能让某个函数独立于其他函数运行的能力.当一个函数创建为 goroutine 时,Golang 会将其视为一个独立的工作单 ...

随机推荐

  1. 沉淀再出发:ElasticSearch的中文分词器ik

    沉淀再出发:ElasticSearch的中文分词器ik 一.前言   为什么要在elasticsearch中要使用ik这样的中文分词呢,那是因为es提供的分词是英文分词,对于中文的分词就做的非常不好了 ...

  2. redis3.2.9编译安装

    Redis 3.2.9 安装 Redis 3.2.9 编译安装 1,   安装相关软件包 2,   下载redis源码包 wget http://source.goyun.org:8000/sourc ...

  3. C++通过Callback向C#传递数据

    现在比较流行C#与C++融合:C#做GUI,开发效率高,C++做运算,运行效率高,二者兼得. 但是C++与C#必然存在数据交互,C#与C++dll的数据交互从来都是一个让人头疼的问题. 从调用方式看也 ...

  4. Python2.7 - IMOOC - 3

    第三章 Python变量和数据类型 3-4.变量 变量名必须是大小写英文.数字和下划线(_)的组合,且不能用数字开头. 同一个变量可以反复赋值,而且可以是不同类型的变量,例如: a = 123 # a ...

  5. php截取后台登陆密码的代码

    php截取后台登陆密码的代码,很多时候黑客留了这样的代码,大家一定要注意下if($_POST[loginsubmit]!=){ //判断是否点了登陆按钮 $sb=user:.$_POST[userna ...

  6. 【OpenCV】三种方式操作图像像素

    OpenCV中,有3种访问每个像素的方法:使用at方法.使用迭代器方法.使用指针 运行如下程序后可以发现使用at方法速度最快. 代码如下: //操作图像像素 #include <opencv2/ ...

  7. μCOS-II系统之事件(event)的使用规则及Semaphore的相互排斥量使用方法

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/wavemcu/article/details/27798493 ****************** ...

  8. UVa 1640 - The Counting Problem(数论)

    链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...

  9. 转:日志组件logback的介绍及配置使用方法

    转自:http://blog.csdn.net/zgmzyr/article/details/8267072 一.logback的介绍 Logback是由log4j创始人设计的又一个开源日志组件.lo ...

  10. fread与fread_s读取文件(二进制文件)

    fread()是c库函数,利于移植,使用缓存,效率较read()高. 原型: size_t fread(void *buffer, size_t size, size_t count, FILE * ...