每日一抄 Go语言通信顺序进程简述
package main
import (
"fmt"
"sync"
)
/*
Go实现了两种并发形式,第一种是大家普遍认知的多线程共享内存,其实就是 Java 或 C++ 等语言中的多线程开发;另外一种是Go语言特有的,也是Go语言推荐的 CSP(communicating sequential processes)并发模型。
CSP 并发模型是上个世纪七十年代提出的,用于描述两个独立的并发实体通过共享 channel(管道)进行通信的并发模型。
Go语言就是借用 CSP 并发模型的一些概念为之实现并发的,但是Go语言并没有完全实现了 CSP 并发模型的所有理论,仅仅是实现了 process 和 channel 这两个概念。
process 就是Go语言中的 goroutine,每个 goroutine 之间是通过 channel 通讯来实现数据共享。
这里我们要明确的是“并发不是并行”。并发更关注的是程序的设计层面,并发的程序完全是可以顺序执行的,只有在真正的多核 CPU 上才可能真正地同时运行;并行更关注的是程序的运行层面,并行一般是简单的大量重复,例如 GPU 中对图像处理都会有大量的并行运算。
为了更好地编写并发程序,从设计之初Go语言就注重如何在编程语言层级上设计一个简洁安全高效的抽象模型,让开发人员专注于分解问题和组合方案,而且不用被线程管理和信号互斥这些烦琐的操作分散精力。
在并发编程中,对共享资源的正确访问需要精确地控制,在目前的绝大多数语言中,都是通过加锁等线程同步方案来解决这一困难问题,而Go语言却另辟蹊径,它将共享的值通过通道传递(实际上多个独立执行的线程很少主动共享资源)。
并发编程的核心概念是同步通信,但是同步的方式却有多种。先以大家熟悉的互斥量 sync.Mutex 来实现同步通信,示例代码如下所示:
*/
/*
由于 mu.Lock() 和 mu.Unlock() 并不在同一个 Goroutine 中,所以也就不满足顺序一致性内存模型。同时它们也没有其他的同步事件可以参考,也就是说这两件事是可以并发的。
因为可能是并发的事件,所以 main() 函数中的 mu.Unlock() 很有可能先发生,而这个时刻 mu 互斥对象还处于未加锁的状态,因而会导致运行时异常。
*/
//func main() {
// var wg sync.WaitGroup
// var mu sync.Mutex
// wg.Add(1)
// go func() {
// defer wg.Done()
// fmt.Println("hello")
// mu.Lock()
// }()
// wg.Wait()
// //如果main的goroutine不等待就会报错
// //time.Sleep(time.Second * 5)
// mu.Unlock()
//}
//这样也能运行
//func main() {
// var mu sync.Mutex
//
// go func() {
// fmt.Println("hello")
// mu.Unlock()
// }()
//
//}
//func main() {
// go func() {
// fmt.Println("hello")
// }()
//}
/*
运行结果
hello
fatal error: sync: unlock of unlocked mutex
*/
//func main() {
// var mu sync.Mutex
// mu.Lock()
// go func() {
// fmt.Println("hello")
// mu.Unlock()
// }()
// mu.Lock()
//}
/*
修复的方式是在 main() 函数所在线程中执行两次 mu.Lock(),当第二次加锁时会因为锁已经被占用(不是递归锁)而阻塞,main() 函数的阻塞状态驱动后台线程继续向前执行。
当后台线程执行到 mu.Unlock() 时解锁,此时打印工作已经完成了,解锁会导致 main() 函数中的第二个 mu.Lock() 阻塞状态取消,此时后台线程和主线程再没有其他的同步事件参考,它们退出的事件将是并发的,在 main() 函数退出导致程序退出时,后台线程可能已经退出了,也可能没有退出。虽然无法确定两个线程退出的时间,但是打印工作是可以正确完成的。
*/
//使用 sync.Mutex 互斥锁同步是比较低级的做法,我们现在改用无缓存通道来实现同步:
//func main() {
// done := make(chan int)
// go func() {
// fmt.Println("hello")
// h := <-done
// fmt.Println("hhhhhhhhhhhhhh", h, '\n')
// }()
// done <- 1
// done <- 2
//}
/*
根据Go语言内存模型规范,对于从无缓存通道进行的接收,发生在对该通道进行的发送完成之前。因此,后台线程<-done 接收操作完成之后,main 线程的done <- 1 发送操作才可能完成(从而退出 main、退出程序),而此时打印工作已经完成了。
上面的代码虽然可以同步,但是对通道的缓存大小太敏感,如果通道有缓存,就无法保证main()函数退出之前后台线程
就能正常打印了,更好的做法是将通道的发送和接收方向调换一下,这样可以避免同步事件受通道缓存大小的影响
*/
//func main() {
// done := make(chan int,1)
// go func() {
// fmt.Println("hello")
// done <- 1
// done <- 2
// }()
// <-done
//}
/*
对于带缓存的通道,对通道的第K个接收完成操作发生在第K+C个发送之前,其中C是通道的的缓存大小。虽然通道是带缓存的。
但是main线程接收完成是在后台线程发送开始但是还未完成的时刻此时打印工作也是已经完成的
*/
//基于带缓存通道,我们可以很容易将打印线程扩展到N个,下面的示例时开启十个后台线程分别打印
//func main() {
// done := make(chan int, 10) //带10个缓存
// /*开启N个后台打印线程*/
// for i := 0; i < cap(done); i++ {
// go func() {
// fmt.Println("hello")
// done <- 1
// }()
// }
//
// //等待N个后台线程完成
// for i := 0; i < cap(done); i++ {
// <-done
// }
//}
//对于这种要等待N个线程完成后再进行下一步的同步操作有一个简单的方法,就是使用sync.WaitGroup来等待一组事件
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("hello")
}()
}
wg.Wait()
}
/*
其中 wg.Add(1) 用于增加等待事件的个数,
必须确保在后台线程启动之前执行(如果放到后台线程之中执行则不能保证被正常执行到)。
当后台线程完成打印工作之后,
调用 wg.Done() 表示完成一个事件,main() 函数的 wg.Wait() 是等待全部的事件完成。
*/
每日一抄 Go语言通信顺序进程简述的更多相关文章
- 09. Go 语言并发
Go 语言并发 并发指在同一时间内可以执行多个任务.并发编程含义比较广泛,包含多线程编程.多进程编程及分布式程序等.本章讲解的并发含义属于多线程编程. Go 语言通过编译器运行时(runtime),从 ...
- 每日一问:简述 View 的绘制流程
Android 开发中经常需要用一些自定义 View 去满足产品和设计的脑洞,所以 View 的绘制流程至关重要.网上目前有非常多这方面的资料,但最好的方式还是直接跟着源码进行解读,每日一问系列一直追 ...
- 利用windows服务+timer或者windows任务计划程序+控制台进行进行每日邮件推送
1.邮件发送代码 using System.Text; using System.Net; using System.Net.Mail; using System.Reflection; using ...
- Beta版本敏捷冲刺每日报告——Day1
1.情况简述 Beta阶段第一次Scrum Meeting 敏捷开发起止时间 2017.11.2 08:00 -- 2017.11.2 21:00 讨论时间地点 2017.11.2晚6:00,软工所实 ...
- 老男孩IT教育-每日一题汇总
老男孩IT教育-每日一题汇总 第几天 第几周 日期 快速访问链接 第123天 第二十五周 2017年8月25日 出现Swap file….already exists以下错误如何解决? 第122天 2 ...
- GoCN每日新闻(2019-11-07)
GoCN每日新闻(2019-11-07) GoCN每日新闻(2019-11-07) 1. [译] 排序运行时间能否做到 O(n)?让 Go 语言来告诉你 https://mp.weixin.qq.co ...
- [每日一题]ES6中为什么要使用Symbol?
关注「松宝写代码」,精选好文,每日面试题 加入我们一起学习,day day up 作者:saucxs | songEagle 来源:原创 一.前言 2020.12.23日刚立的flag,每日一题,题目 ...
- 《MySQL面试小抄》查询缓存机制终面
<MySQL面试小抄>查询缓存机制终面 我是肥哥,一名不专业的面试官! 我是囧囧,一名积极找工作的小菜鸟! 囧囧表示:小白面试最怕的就是面试官问的知识点太笼统,自己无法快速定位到关键问题点 ...
- 《MySQL面试小抄》索引考点二面总结
<MySQL面试小抄>索引考点二面总结 我是肥哥,一名不专业的面试官! 我是囧囧,一名积极找工作的小菜鸟! 囧囧表示:小白面试最怕的就是面试官问的知识点太笼统,自己无法快速定位到关键问题点 ...
- 【Java每日一题】20170106
20170105问题解析请点击今日问题下方的"[Java每日一题]20170106"查看(问题解析在公众号首发,公众号ID:weknow619) package Jan2017; ...
随机推荐
- 在TMOS系统中添加按键检测功能
目录 TMOS系统中自带有按键检测轮询功能,默认100ms检测一次,在debug时很有帮助.不过在需要低功耗休眠的前提下,检测按键需要频繁唤醒,影响功耗,故多数BLE的工程都没有用到这个功能.代码贴在 ...
- Teamcity 部署
1.先安装docker环境 team_city安装 2.docker run -it --name teamcity-server \ -v /data/teamctiy_data/:/data/te ...
- 关于Python 面向对象寻值的问题. How the number be found in the OOP in Python
今天在看Python面向对象的时候看到了一个很有意思的问题 Today. When i learning the OOP in python , I found a very interesting ...
- django项目 在进行数据生成迁移文件makemigrations时报Please select a fix:...
问题:django项目 在进行生成迁移文件:python .\manage.py makemigrations时,报错,如图 原因:在之前项目新建模型的时候,缺少一个字段进行迁移了然后数据表中又产生了 ...
- 计算机视觉——SSD和YOLO简介
前言 本文记录用,防止遗忘 计算机视觉--SSD和YOLO简介 课件(单发多框检测SSD) 生成锚框 对每个像素,生成多个以它为中心的锚框 给定n个大小 s1, ...,s2,和m个高宽比,那么生成 ...
- 基于 geojson数据类型面转线Transforms Polygons and MultiPolygons to LineStrings.
function flatten(array) { return [].concat.apply([], array); } function polygonToLineString(coordina ...
- idea等工具网盘下载地址
1.idea2020 下载地址:https://caiyun.139.com/m/i?1E5C2SkIZbJH4 ,下载密码微信 搜索 "白菜拼吧" 回复 idea2020 获取 ...
- shell_Day09
awk [单独的编程语言解释器] awk介绍 全称:Aho Weinberger Kernaighan 三个人的首字母缩写: 1970年第一次出现在Unix机器上,后来在开源领域使用它: 所以,我们在 ...
- .什么是 SPA 单页面,它的优缺点分别是什么
SPA( single-page application )即一个web项目就只有一个页面(即一个HTML文件,HTML 内容的变换是利用路由机制实现的. 仅在 Web 页面初始化时加载相应的 HTM ...
- 【当年笔记】Collection集合部分
集合继承关系图 1)Vector 特点:线程安全,消耗偏大 2)ArrayList 特点:基于数组实现,随机访问某个元素效率高.集和头尾之间包括头插入删除操作效率较低,因为插入元素后,其他元素要后移. ...