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唯一一次。

原文发布于微信公众号 - Golang语言社区(Golangweb)

原文发表时间:2016-09-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏TechBox

【iOS】运行时消息传递与转发机制前言(一)对象的消息传递机制 objc_msgSend()(二)消息转发流程参考文章

1314
来自专栏JavaQ

高并发编程-Condition深入解析

Condition接口位于java.util.concurrent.locks包下,实现类有 AbstractQueuedLongSynchronizer.Co...

984
来自专栏陈树义

Java并发编程:synchronized

Java并发编程:synchronized   虽然多线程编程极大地提高了效率,但是也会带来一定的隐患。比如说两个线程同时往一个数据库表中插入不重复的数据,就可...

3494
来自专栏散尽浮华

linux运维中的命令梳理(三)

----------文本操作命令---------- sed命令:文本编辑工具 sed是一个很好的文件处理工具,本身是一个管道命令,主要是以行为单位进行处理,可...

2558
来自专栏zhisheng

Guava Cache 用法介绍

Guava Cache是在内存中缓存数据,相比较于数据库或redis存储,访问内存中的数据会更加高效。Guava官网介绍,下面的这几种情况可以考虑使用Guava...

962
来自专栏Java技术栈

JDK9新特性实战:简化流关闭新姿势。

做Java开发的都知道,每个资源的打开都需要对应的关闭操作,不然就会使资源一直占用而造成资源浪费,从而降低系统性能。 关于资源的关闭操作,从JDK7-JDK9有...

3588
来自专栏企鹅号快讯

用 Python 处理 HTML 转义字符的5种方式

Photo byAhmed SaffuonUnsplash 写爬虫是一个发送请求,提取数据,清洗数据,存储数据的过程。在这个过程中,不同的数据源返回的数据格式各...

4089
来自专栏码代码的陈同学

Java中的类加载器

Class loaders属于JRE的一部分,负责在运行时将Java类动态加载到JVM。得益于class loaders,JVM在无需知晓底层文件或文件系统时就...

1172
来自专栏IT技术精选文摘

深入理解 Java 并发之 synchronized 实现原理

线程安全是并发编程中的重要关注点,应该注意到的是,造成线程安全问题的主要诱因有两点,一是存在共享数据(也称临界资源),二是存在多条线程共同操作共享数据。因此为了...

4657
来自专栏Golang语言社区

go rpc 源码分析

go 源码中带了rpc框架,以相对精简的当时方式实现了rpc功能,目前源码中的rpc官方已经宣布不再添加新功能,并推荐使用grpc. 作为go标准库中rpc框架...

1994

扫码关注云+社区

领取腾讯云代金券