版权声明:本文为博主原创文章,未经博主允许不得转载。如需转载请联系本人,并标明作者和出处。 https://blog.csdn.net/huwh_/article/details/74858134

本文个人博客地址:http://www.huweihuang.com/article/golang/golang-concurrent-programming/

(一)并发基础

1.概念

并发意味着程序在运行时有多个执行上下文,对应多个调用栈。

并发与并行的区别:

并发的主流实现模型:

实现模型
说明
特点
多进程 操作系统层面的并发模式 处理简单,互不影响,但开销大
多线程 系统层面的并发模式 有效,开销较大,高并发时影响效率
基于回调的非阻塞/异步IO 多用于高并发服务器开发中 编程复杂,开销小
协程 用户态线程,不需要操作系统抢占调度,寄存于线程中 编程简单,结构简单,开销极小,但需要语言的支持

共享内存系统:线程之间采用共享内存的方式通信,通过加锁来避免死锁或资源竞争。

消息传递系统:将线程间共享状态封装在消息中,通过发送消息来共享内存,而非通过共享内存来通信。

2.协程

执行体是个抽象的概念,在操作系统中分为三个级别:进程(process),进程内的线程(thread),进程内的协程(coroutine,轻量级线程)。协程的数量级可达到上百万个,进程和线程的数量级最多不超过一万个。Go语言中的协程叫goroutine,Go标准库提供的调用操作,IO操作都会出让CPU给其他goroutine,让协程间的切换管理不依赖系统的线程和进程,不依赖CPU的核心数量。

3.并发通信

并发编程的难度在于协调,协调需要通过通信,并发通信模型分为共享数据和消息。共享数据即多个并发单元保持对同一个数据的引用,数据可以是内存数据块,磁盘文件,网络数据等。数据共享通过加锁的方式来避免死锁和资源竞争。Go语言则采取消息机制来通信,每个并发单元是独立的个体,有独立的变量,不同并发单元间这些变量不共享,每个并发单元的输入输出只通过消息的方式。

(二)goroutine

//定义调用体
func Add(x,y int){
z:=x+y
fmt.Println(z)
}
//go关键字执行调用,即会产生一个goroutine并发执行
//当函数返回时,goroutine自动结束,如果有返回值,返回值会自动被丢弃
go Add(1,1)
//并发执行
func main(){
for i:=0;i<10;i++{//主函数启动了10个goroutine,然后返回,程序退出,并不会等待其他goroutine结束
go Add(i,i) //所以需要通过channel通信来保证其他goroutine可以顺利执行
}
}

(三)channel

channel就像管道的形式,是goroutine之间的通信方式,是进程内的通信方式,跨进程通信建议用分布式系统的方法来解决,例如Socket或http等通信协议。channel是类型相关,即一个channel只能传递一种类型的值,在声明时指定。

1、基本语法

//1、channel声明,声明一个管道chanName,该管道可以传递的类型是ElementType
//管道是一种复合类型,[chan ElementType],表示可以传递ElementType类型的管道[类似定语从句的修饰方法]
var chanName chan ElementType
var ch chan int //声明一个可以传递int类型的管道
var m map[string] chan bool //声明一个map,值的类型为可以传递bool类型的管道
//2、初始化
ch:=make(chan int) //make一般用来声明一个复合类型,参数为复合类型的属性
//3、管道写入,把值想象成一个球,"<-"的方向,表示球的流向,ch即为管道
//写入时,当管道已满(管道有缓冲长度)则会导致程序堵塞,直到有goroutine从中读取出值
ch <- value
//管道读取,"<-"表示从管道把球倒出来赋值给一个变量
//当管道为空,读取数据会导致程序阻塞,直到有goroutine写入值
value:= <-ch
//4、每个case必须是一个IO操作,面向channel的操作,只执行其中的一个case操作,一旦满足则结束select过程
//面向channel的操作无非三种情况:成功读出;成功写入;即没有读出也没有写入
select{
case <-chan1:
//如果chan1读到数据,则进行该case处理语句
case chan2<-1:
//如果成功向chan2写入数据,则进入该case处理语句
default:
//如果上面都没有成功,则进入default处理流程
}

2、缓冲和超时机制

//1、缓冲机制:为管道指定空间长度,达到类似消息队列的效果
c:=make(chan int,1024) //第二个参数为缓冲区大小,与切片的空间大小类似
//通过range关键字来实现依次读取管道的数据,与数组或切片的range使用方法类似
for i :=range c{
fmt.Println("Received:",i)
}
//2、超时机制:利用select只要一个case满足,程序就继续执行而不考虑其他case的情况的特性实现超时机制
timeout:=make(chan bool,1) //设置一个超时管道
go func(){
time.Sleep(1e9) //设置超时时间,等待一秒钟
timeout<-true //一分钟后往管道放一个true的值
}()
//
select {
case <-ch: //如果读到数据,则会结束select过程
//从ch中读取数据
case <-timeout: //如果前面的case没有调用到,必定会读到true值,结束select,避免永久等待
//一直没有从ch中读取到数据,但从timeout中读取到了数据
}

3、channel的传递

//1、channel的传递,来实现Linux系统中管道的功能,以插件的方式增加数据处理的流程
type PipeData struct{
value int
handler func(int) int //handler是属性?
next chan int //可以把[chan int]看成一个整体,表示放int类型的管道
}
func handler(queue chan *PipeData){ //queue是一个存放*PipeDate类型的管道,可改变管道里的数据块内容
for data:=range queue{ //data的类型就是管道存放定义的类型,即PipeData
data.next <- data.handler(data.value) //该方法实现将PipeData的value值存放到next的管道中
}
}
//2、单向channel:只能用于接收或发送数据,是对channel的一种使用限制
//单向channel的声明
var ch1 chan int //正常channel,可读写
var ch2 chan<- int //单向只写channel [chan<- int]看成一个整体,表示流入管道
var ch3 <-chan int //单向只读channel [<-chan int]看成一个整体,表示流出管道
//管道类型强制转换
ch4:=make(chan int) //ch4为双向管道
ch5:=<-chan int(ch4) //把[<-chan int]看成单向只读管道类型,对ch4进行强制类型转换
ch6:=chan<- int(ch4) //把[chan<- int]看成单向只写管道类型,对ch4进行强制类型转换
func Parse(ch <-chan int){ //最小权限原则
for value:=range ch{
fmt.Println("Parsing value",value)
}
}
//3、关闭channel,使用内置函数close()函数即可
close(ch)
//判断channel是否关闭
x,ok:=<-ch //ok==false表示channel已经关闭
if !ok { //如果channel关闭,ok==false,!ok==true
//执行体
}

(四)多核并行化与同步

//多核并行化
runtime.GOMAXPROCS(16) //设置环境变量GOMAXPROCS的值来控制使用多少个CPU核心
runtime.NumCPU() //来获取核心数
//出让时间片
runtime.Gosched() //在每个goroutine中控制何时出让时间片给其他goroutine
//同步
//同步锁
sync.Mutex //单读单写:占用Mutex后,其他goroutine只能等到其释放该Mutex
sync.RWMutex //单写多读:会阻止写,不会阻止读
RLock() //读锁
Lock() //写锁
RUnlock() //解锁(读锁)
Unlock() //解锁(写锁)
//全局唯一性操作
//once的Do方法保证全局只调用指定函数(setup)一次,其他goroutine在调用到此函数是会阻塞,直到once调用结束才继续
once.Do(setup)

Golang系列(三)之并发编程相关推荐

  1. php三要素,并发编程三要素:原子性,有序性,可见性

    并发编程三要素 **原子性:**一个不可再被分割的颗粒.原子性指的是一个或多个操作要么全部执行成功要么全部执行失败. 有序性: 程序执行的顺序按照代码的先后顺序执行.(处理器可能会对指令进行重排序) ...

  2. python多线程并发编程技术_三 python并发编程之多线程-理论

    一 什么是线程 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程 车间负责把资源整合 ...

  3. java高并发(三)并发编程的基础

    CPU多级缓存 为什么需要CPU缓存? 原因是,CPU的频率太快了,快到主存跟不上,这样在处理器时钟周期内,CPU常常需要等待主存,浪费资源.所以cache的出现,是为了缓解CPU和内存之间速度的不匹 ...

  4. golang经典书籍--go并发编程

  5. Java并发编程系列

    Java并发编程系列 2018-03-08 Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) ...

  6. Java多线程系列(七):并发容器的原理,7大并发容器详解、及使用场景

    之前谈过高并发编程系列: 高并发编程系列:4种常用Java线程锁的特点,性能比较.使用场景 高并发编程系列:CountDownLatch.Semaphore等4大并发工具类详解 高并发编程系列:4大J ...

  7. Java并发编程|第二篇:线程生命周期

    文章目录 系列文章 1.线程的状态 2.线程生命周期 3.状态测试代码 4.线程终止 4.1 线程执行完成 4.2 interrupt 5.线程复位 5.1interrupted 5.2抛出异常 6. ...

  8. 并发编程(七)好用的线程池ThreadPoolExecutor

    并发编程专栏系列博客 并发编程(一)python并发编程简介 并发编程(二)怎样选择多线程多进程和多协程 并发编程(三)Python编程慢的罪魁祸首.全局解释器锁GIL 并发编程(四)如何使用多线程, ...

  9. 并发编程(五)python实现生产者消费者模式多线程爬虫

    并发编程专栏系列博客 并发编程(一)python并发编程简介 并发编程(二)怎样选择多线程多进程和多协程 并发编程(三)Python编程慢的罪魁祸首.全局解释器锁GIL 并发编程(四)如何使用多线程, ...

  10. 阿里蚂蚁金服Java岗330道面试题(性能调优+微服务+并发编程+开源框架+分布式)

    前言 2019年还有不到2个月的时间就结束了,这一你,你收获了多少?  前段时间一直有粉丝问我,有没有今年一些大厂Java面试题总结?最新抽时间整理了一些,分享给大家,大家一起共享学习! 一.性能调优 ...

最新文章

  1. C#帮助类:MD5加密
  2. MITRE:利用微生物组时间序列数据推断与宿主状态变化相关的特征
  3. spring boot的热加载(hotswap)
  4. Maven整合SSM框架(maven+spring+springmvc+mybatis)
  5. 牛客多校第六场-H-Pair
  6. 计算机网络基础知识,仅此一篇足矣
  7. jquery html data属性,jQuery Mobile Data 属性
  8. linux禁用IPv6地址
  9. TensorFlow tf.keras.callbacks.CSVLogger
  10. 下列哪个适合做链栈_外贸企业如何做Google推广?自然排名和付费广告哪个更适合你?...
  11. vector的常用总结
  12. Android 开发性能优化
  13. Chrome 地址栏如何设置显示 http/https 和 www
  14. 制衣软件测试自学,服装检验作业指导书.doc
  15. python发送短信验证码(互亿无线)
  16. php 常见的视频格式转换
  17. MATLAB中load用法
  18. TVS 瞬态抑制二极管指南
  19. leetcode 5855. 找出数组中的第 K 大整数(C++、java、python)
  20. win10便签常驻桌面_做备忘录,用win10自带的便笺工具就可以了,免费又方便

热门文章

  1. sql语句 isnull(列名,'')='' /STUFF的意思
  2. websql使用实例
  3. logistics模型的训练
  4. DevExpress学习笔记之如何获取Repository Item的值
  5. JGrid有用的收藏
  6. 增值业务综合运营平台(VGOP)
  7. 【python】Python的基本数据类型之数字类型与字符串类型
  8. 线程安全的HashMap,TreeMap,ArrayList,TreeSet,Set
  9. JMETER SLAVE和MASTER 分布式启动压测
  10. docker hub push_Docker系列-(2) 镜像制作与发布