Go语言同步(Synchronization)
Go语言同步(Synchronization)
1. 初始化
程序的初始化在一个独立的goroutine中执行。在初始化过程中创建的goroutine将在 第一个用于初始化goroutine执行完成后启动。
如果包p导入了包q,包q的init 初始化函数将在包p的初始化之前执行。
程序的入口函数 main.main 则是在所有的 init 函数执行完成 之后启动。
在任意init函数中新创建的goroutines,将在所有的init 函数完成后执行。
2. Goroutine的创建
用于启动goroutine的go语句在goroutine之前运行。
例如,下面的程序:
var a string;
func f() {
print(a);
}
func hello() {
a = "hello, world";
go f();
}
调用hello函数,会在某个时刻打印“hello, world”(有可能是在hello函数返回之后)。
3. Channel communication 管道通信
用管道通信是两个goroutines之间同步的主要方法。在管道上执行的发送操作会关联到该管道的 接收操作,这通常对应goroutines。
管道上的发送操作发生在管道的接收完成之前(happens before)。
例如这个程序:
var c = make(chan int, 10)
var a string
func f() {
a = "hello, world";
c <- 0;
}
func main() {
go f();
<-c;
print(a);
}
可以确保会输出"hello, world"。因为,a的赋值发生在向管道 c发送数据之前,而管道的发送操作在管道接收完成之前发生。 因此,在print 的时候,a已经被赋值。
从一个unbuffered管道接收数据在向管道发送数据完成之前发送。
下面的是示例程序:
var c = make(chan int)
var a string
func f() {
a = "hello, world";
<-c;
}
func main() {
go f();
c <- 0;
print(a);
}
同样可以确保输出“hello, world”。因为,a的赋值在从管道接收数据 前发生,而从管道接收数据操作在向unbuffered 管道发送完成之前发生。所以,在print 的时候,a已经被赋值。
如果用的是缓冲管道(如 c = make(chan int, 1) ),将不能保证输出 “hello, world”结果(可能会是空字符串, 但肯定不会是他未知的字符串, 或导致程序崩溃)。
4. 锁
包sync实现了两种类型的锁: sync.Mutex 和 sync.RWMutex。
对于任意 sync.Mutex 或 sync.RWMutex 变量l。 如果 n < m ,那么第n次 l.Unlock() 调用在第 m次 l.Lock() 调用返回前发生。
例如程序:
var l sync.Mutex
var a string
func f() {
a = "hello, world";
l.Unlock();
}
func main() {
l.Lock();
go f();
l.Lock();
print(a);
}
可以确保输出“hello, world”结果。因为,第一次 l.Unlock() 调用(在f函数中)在第二次 l.Lock() 调用 (在main 函数中)返回之前发生,也就是在 print 函数调用之前发生。
For any call to l.RLock on a sync.RWMutex variable l, there is an n such that the l.RLock happens (returns) after the n'th call to l.Unlock and the matching l.RUnlock happens before the n+1'th call to l.Lock.
9.3.5. Once
包once提供了一个在多个goroutines中进行初始化的方法。多个goroutines可以 通过 once.Do(f) 方式调用f函数。 但是,f函数 只会被执行一次,其他的调用将被阻塞直到唯一执行的f()返回。
once.Do(f) 中唯一执行的f()发生在所有的 once.Do(f) 返回之前。
有代码:
var a string
func setup() {
a = "hello, world";
}
func doprint() {
once.Do(setup);
print(a);
}
func twoprint() {
go doprint();
go doprint();
}
调用twoprint会输出“hello, world”两次。第一次twoprint 函数会运行setup唯一一次。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Vue3.0源码结构分析
- 【每周一库】- cached - 缓存结构型、辅助函数记忆化
- meta生成器 —— 表单元素组件 meta表单代码meta的模板data变幻
- 不用写代码也能做表单 —— 加载meta即可 菜单表单加载json运行效果。ModelAbout
- 从0到1,手把手教你入门 etcd
- 数据结构:手把手带你了解 ”图“ 所有知识!(含DFS、BFS)
- 设计模式之单例模式
- AndroidStudio创建JNI 工程与调用
- Java 版植物大战僵尸思路和源码分享!
- 你好MyBatis 入门篇
- 你好MyBatis 中级篇
- 你好MyBatis 中高级篇
- Nginx反代理获取真实IP
- 你好MyBatis 高级篇
- redis实现分布式锁:他说,他的分布式锁,很润哦