Golang并发编程基础
硬件
内存
作为并发编程一个基础硬件知识储备,首先要说的就是内存了,总的来说在绝大多数情况下把内存的并发增删改查模型搞清楚了其他的基本上也是异曲同工之妙。
内存芯片——即我们所知道的内存颗粒,是一堆MOS管的集合,在半导体称呼里面,很多MOS管组成一个半导体(组module),很多个module组成一个管芯(die),这个die即是内存颗粒,当然,更上一级即很多die组成的东西叫做晶圆(wafer)。
简单来说,每8个MOS管组成的电路可以表示一个字节,比如ASCII的‘A’,我们使用65表示,即0100 0001,那么8个MOS分别使用低-高-低-低-低-低-低-高电位即可表示字符A。
在对内存的写入和读取时,通常也是按照8个字开始作为一组进行操作,我们现在常用的CPU是64位,可以一次性处理64/8=8个字节的数据。
总线
首先明确一个概念:总线是线但是也不是线,以下是来自百科的解释:
总线(Bus)是计算机各种功能部件之间传送信息的公共通信干线,它是由导线组成的传输线束, 按照计算机所传输的信息种类,计算机的总线可以划分为数据总线、地址总线和控制总线,分别用来传输数据、数据地址和控制信号。总线是一种内部结构,它是cpu、内存、输入、输出设备传递信息的公用通道。
一个CPU要操作内存的数据,是通过总线来进行操作的,通常来说内存的读写操作不是一个CPU指令周期能完成的,如果多个程序在同时操作一个内存地址,则有各种意外的读写操作。
CPU
在单核CPU时期,硬件一次只能处理一个事情,在多任务的情况下不同的任务按需抢占CPU来执行它的代码,这里面就涉及到CPU调度工作,通常情况下,操作系统已经帮我们做了很多事,如果一个编程语言开启的并发操作是交给了操作系统的,那么调度这块不需要太关心,如果像Go这样有自己的协程调度器,还是需要专门了解下特有的调度方式的。
多核时期,基本原理也差不多,在对于硬件的理解上也可以完全参考单核。
CPU通过地址总线去寻找内存地址,比如0x00004567这种,64位CPU最大能操作的地址长度为264,32位操作系统则是232长度,所以为什么32位CPU最大只支持4GB内存呢?
几个代码示例
示例一
package main
import (
"fmt"
)
var A int
func main() {
A = 0
for i:=0;i<100;i++{
A++
}
fmt.Println(A)
}
示例二
package main
import (
"fmt"
"time"
)
var A int
func main() {
A = 0
for j:=0;j<100;j++{
go add()
}
time.Sleep(1*time.Second)
fmt.Println(A)
}
func add(){
A++
return
}
示例一个示例二都将输出什么呢,直接告诉大家结果吧:绝大多数情况下都是100
那么go的协程难道这么听话,我们就完全很happy地编码了吗?先把示例二的100改成10000再试试吧_
我们再看看示例三和示例四:
示例三
package main
import (
"fmt"
)
func main() {
for i:=0;i<10000;i++{
fmt.Println(i)
}
}
示例四
package main
import (
"fmt"
"time"
)
func main() {
for j:=0;j<10000;j++{
go add(j)
}
time.Sleep(1*time.Second)
}
func add(j int){
fmt.Println(j)
return
}
示例三其实没太多好说的,单协程模型,输出也不会有什么意外,而示例四大家猜猜是按照1,2,3...9999这样的顺序呢还是其他输出顺序呢?
综上结果,我们会发现多协程模型里面的东西没有顺序性,对变量的操作也没有原子性。
示例五给出了Golang中最简单的加锁处理方式:
示例五
package main
import (
"fmt"
"time"
"sync"
)
var A int
var LOCK *sync.Mutex
func main() {
A = 0
LOCK = new(sync.Mutex)
for j:=0;j<10000;j++{
go add()
}
time.Sleep(1*time.Second)
fmt.Println(A)
}
func add(){
LOCK.Lock()
A++
LOCK.Unlock()
return
}
而关于多协程顺序性方面的实现方式,也可以比着葫芦画瓢写出来,这里就不再赘述了。
搬砖例子
假设在左边有三堆散乱的砖,我们需要将其从左边搬运到右边并堆放整齐,这样的一个工作我们从并发模型来看有哪些比较可执行的实现方式呢:
- 每堆砖头分配固定的人数,堆砖时为保证堆叠整齐度,采用排队的方式一个一个按先后顺序堆叠
- 拿一个人专职在左边递砖,若干人从左边的递砖人处拿砖,搬砖后在右边排队堆叠
- 左边专人递砖,右边专人堆砖,若干搬砖人只负责搬砖
这也是并发编程模型中比较常用的编程思路,在以后遇到类似问题的时候可以想想这个例子。
一个实际案例
我们以一个实际的案例作为结束,这个案例是导出某云平台所属设备信息的代码,里面包含有多协程拉取数据的实例,整体的流程如下:
- 参数初始化
- 定义一个接收协程结束的信息通道
- 开启N个协程
- 协程调用API获取信息,按分页参数每个协程获取(总数/N)信息,每次page=X+N
- 每次获取的信息放入excel缓冲区
- 当最后的分页获取不到信息时向通道写入东西表示该协程任务完成
- 主进程循环获取每个协程结束的信息,直到所有协程任务完成
- 将excel缓冲区数据写入excel文件
- 结束
连接如下:
https://github.com/cm-heclouds/onenet_device_export/releases/tag/2018-latest
当然,这个案例在并发上其实还存在较大的提升空间,聪明的大家看看结合搬砖的例子来怎么提升呢。
Golang并发编程基础的更多相关文章
- golang并发编程
golang并发编程 引子 golang提供了goroutine快速实现并发编程,在实际环境中,如果goroutine中的代码要消耗大量资源时(CPU.内存.带宽等),我们就需要对程序限速,以防止go ...
- python中并发编程基础1
并发编程基础概念 1.进程. 什么是进程? 正在运行的程序就是进程.程序只是代码. 什么是多道? 多道技术: 1.空间上的复用(内存).将内存分为几个部分,每个部分放入一个程序,这样同一时间在内存中就 ...
- Java并发编程基础
Java并发编程基础 1. 并发 1.1. 什么是并发? 并发是一种能并行运行多个程序或并行运行一个程序中多个部分的能力.如果程序中一个耗时的任务能以异步或并行的方式运行,那么整个程序的吞吐量和可交互 ...
- TCP与UDP比较 以及并发编程基础知识
一.tcp比udp真正可靠地原因 1.为什么tcp比udp传输可靠地原因: 我们知道在传输数据的时候,数据是先存在操作系统的缓存中,然后发送给客户端,在客户端也是要经过客户端的操作系统的,因为这个过程 ...
- Golang - 并发编程
目录 Golang - 并发编程 1. 并行和并发 2. go语言并发优势 3. goroutine是什么 4. 创建goroutine 5. runtime包 6. channel是什么 7. ch ...
- 并发-Java并发编程基础
Java并发编程基础 并发 在计算机科学中,并发是指将一个程序,算法划分为若干个逻辑组成部分,这些部分可以以任何顺序进行执行,但与最终顺序执行的结果一致.并发可以在多核操作系统上显著的提高程序运行速度 ...
- Java并发编程系列-(1) 并发编程基础
1.并发编程基础 1.1 基本概念 CPU核心与线程数关系 Java中通过多线程的手段来实现并发,对于单处理器机器上来讲,宏观上的多线程并行执行是通过CPU的调度来实现的,微观上CPU在某个时刻只会运 ...
- Java并发编程基础三板斧之Semaphore
引言 最近可以进行个税申报了,还没有申报的同学可以赶紧去试试哦.不过我反正是从上午到下午一直都没有成功的进行申报,一进行申报 就返回"当前访问人数过多,请稍后再试".为什么有些人就 ...
- Java高并发编程基础三大利器之CountDownLatch
引言 上一篇文章我们介绍了AQS的信号量Semaphore<Java高并发编程基础三大利器之Semaphore>,接下来应该轮到CountDownLatch了. 什么是CountDownL ...
随机推荐
- Ubuntu18.04安装Docker并部署(编译、发布、构建镜像)Asp.NetCore项目全过程笔记
环境准备:阿里云Ubuntu18.04 全新安装 一.安装Docker 1.删除旧版本并更新包索引: sudo apt-get remove docker docker-engine dock ...
- IOS上传图片方向问题
在显示上传完毕的图片的时候遇到了一个问题, 图片莫名其妙被逆时针旋转了90度就很离谱 如下图 经过一番查询, 原来是 IOS 的相机拍照的时候会把方向角写入到图片里面 因为我用的是 element 的 ...
- 高精度进制转换(poj1220)
常规短除法原理 高精度进制转换是对于特别大的数字来说的,当数字特别大时,难以进行除法和取余的操作,此时通过字符串模拟的办法可以解决. #include <iostream> #includ ...
- 阿里云OSS服务器的使用
关于文件上传,我们一般使用OSS服务器.大致为两种上传方式: 详情官网参考:https://help.aliyun.com/document_detail/31927.html?spm=a2c4g.1 ...
- 网络通信机制:Socket、TCP/IP、HTTP
13.1.1 TCP/IP协议 讲的很抽象,没具体看懂什么是TCP协议,什么是IP协议.IP协议保证消息从一个主机传送到另一个主机,消息在传送的过程中被分割成一个个小包,TCP协议会让两台相互连接的计 ...
- redis基本操作介绍
一.字符串 单个设置:set key value,如果key不存在则设置键值对,如果存在则修改 批量设置:mset key1 value1 [key2 value2] 单个获取:get key,如果k ...
- Jquery日历编写小练习
日历练习 总体效果展示: 代码展示: 源代码部分 <body> <!-- 日历--> <div class="div_sty"> <tab ...
- Java数据结构和算法(1)之队列
1.队列的基本概念 队列(queue)是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表.进行插 ...
- Django学习路2
1.导入 Django 工程时 在 manage.py 上一级目录导入 2.Add local 本地 Add Remote 远端 3.BASE_DIR 项目所在目录的绝对路径 4.SECRET_KEY ...
- 不想得手指关节炎?帮你提炼IDEA常用代码补全操作
一.常用的代码补全操作 1..for和.fori(for 循环遍历) 输入args.for回车(args是一个数组或集合类),则会生成for循环遍历: 输入args.fori回车,则会生成带有索引的f ...