前言:

在前面并发性能对比的文章中,我们可以看到Golang处理大并发的能力十分强劲,而且开发也特别方便,只需要用go关键字即可开启一个新的协程。

但当多个goroutine同时进行处理的时候,就会遇到同时抢占一个资源的情况(并发都会遇到的问题),所以我们希望某个goroutine等待另一个goroutine处理完某一个步骤之后才能继续。sync包就是为了让goroutine同步而出现的。当然还可以使用channel实现,这个后面会介绍到。

锁:

锁有两种:互斥锁(mutex)和读写锁(RWMutex)

互斥锁: 当数据被加锁了之后,除次外的其他协程不能对数据进行读操作和写操作。 这个当然能解决并发程序对资源的操作。但是,效率上是个问题,因为当加锁后,其他协程只有等到解锁后才能对数据进行读写操作。

读写锁: 读数据的时候上读锁,写数据的时候上写锁。有写锁的时候,数据不可读不可写。有读锁的时候,数据可读,不可写。

两种锁的使用方式相同,这里就只列出互斥锁的代码:

package main

import (
  "sync"
  "time"
  "fmt"
) var num = 0 func main () {
  mu := &sync.Mutex{}
  for i:=0;i<10000;i++ {
    go func(){
      mu.Lock()
      defer mu.Unlock()
      num += 1
    }()
  }
  time.Sleep(time.Second)
  fmt.Println("num:", num) // 如果不加锁这里的num的值会是一个随机数而不是10000
}

Once:

有的时候,我们启动多个相同goroutine,但是里面的某个操作我只希望被执行一次,这个时候Once就上场了。

package main

import (
  "fmt"
  "sync"
  "time"
) func main() {
  var once sync.Once
  one := func() {
fmt.Println("just once")
  }   for i := 0; i < 10; i++ {
go func(a int) {
  once.Do(one) // 只是被执行一次
}(i)
  }
  time.Sleep(time.Millisecond*200)
}

WaitGroup:

当某个操作或是某个goroutine需要等待一批goroutine执行完毕以后才继续执行,那么这种多线程(go里面说的线程就是goroutine)等待的问题就可以使用WaitGroup了。

代码如下:

package main

import (
"sync"
"fmt"
"time"
) var waitGroup sync.WaitGroup func main () {
for i := 0; i < 10; i++ {
waitGroup.Add(1) // 添加需要等待goroutine的数量
go func() {
fmt.Println("hehe")
time.Sleep(time.Second)
waitGroup.Done() // 减少需要等待goroutine的数量 相当于Add(-1)
} ()
} waitGroup.Wait() // 执行阻塞,直到所有的需要等待的goroutine数量变成0
fmt.Println("over")
}

Cond:

sync.Cond是用来控制某个条件下,goroutine进入等待时期,等待信号到来,然后重新启动。

代码如下:

package main

import (
"fmt"
"sync"
"time"
)
var locker = new(sync.Mutex)
var cond = sync.NewCond(locker) func test(x int) {
cond.L.Lock() //获取锁
cond.Wait()//等待通知 暂时阻塞
fmt.Println(x)
time.Sleep(time.Second * 1)
cond.L.Unlock()//释放锁
}
func main() {
for i := 0; i < 40; i++ {
go test(i)
}
fmt.Println("start all")
time.Sleep(time.Second * 3)
fmt.Println("signal1")
cond.Signal() // 下发一个通知随机给已经获取锁的goroutine
time.Sleep(time.Second * 3)
fmt.Println("signal2")
cond.Signal()// 下发第二个通知随机给已经获取锁的goroutine
time.Sleep(time.Second * 1) // 在广播之前要等一会,让所有线程都在wait状态
fmt.Println("broadcast")
cond.Broadcast()//下发广播给所有等待的goroutine
time.Sleep(time.Second * 60)
}

上面代码有几个要点要特别说明一下:

1. 每个Cond都必须有个与之关联的锁  // 见第9行

2. 协程方法里面一开始/结束都必须加/解锁 // 见第12行和16行

3. cond.Wait()时会自动解锁,当被唤醒时,又会加上锁。所以第2点提到必须加/解锁。

Channel

channel不仅可以用来goroutine之间的通信,也可以使goroutine同步完成协作。这点主要基于从channel取数据的时候,会阻塞当前goroutine这个特性。示例代码如下:

package main

import (
"fmt"
"time"
) var chan1 = make(chan string, 512) var arr1 = []string{"qq","ww","ee","rr","tt"} func chanTest1() {
for _, v := range arr1 {
chan1 <- v
}
close(chan1) // 关闭channel
} func chanTest2() {
for {
getStr, ok := <- chan1 // 阻塞,直到chan1里面有数据
if !ok { // 判断channel是否关闭或者为空
return
}
fmt.Println(getStr) // 按数组顺序内容输出
}
} func main () {
go chanTest1()
go chanTest2() time.Sleep(time.Millisecond*200)
}

使goroutine同步的方法总结的更多相关文章

  1. JAVA中线程同步的方法(7种)汇总

    同步的方法: 一.同步方法 即有synchronized关键字修饰的方法. 由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法.在调用该方法前,需要获得内置锁,否则就 ...

  2. Java中线程同步的方法

    同步方法 即有synchronized关键字修饰的方法. 由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法.在调用该方法前,需要获得内置锁,否则就处于阻塞状态. 注 ...

  3. golang-----golang sync.WaitGroup解决goroutine同步

    go提供了sync包和channel来解决协程同步和通讯.新手对channel通道操作起来更容易产生死锁,如果时缓冲的channel还要考虑channel放入和取出数据的速率问题. 从字面就可以理解, ...

  4. Sqlserver2005日志文件太大,使其减小的方法

    Sqlserver2005日志文件太大,使其减小的方法: 运行下面的三行 dbName为数据库名: backup log dbNamewith NO_LOG backup log dbNamewith ...

  5. 使用UEditor 的时候,ajax注意使用同步的方法

    使用UEditor 的时候,ajax注意使用同步的方法去读取后台数据,然后填写到前端的文本域当中.

  6. iOS网络编程同步GET方法请求编程

    iOS SDK为HTTP请求提供了同步和异步请求两种不同的API,而且可以使用GET或POST等请求方法.我们先了解其中最为简单的同步GET方法请求. 首先实现查询业务,查询业务请求可以在主视图控制器 ...

  7. Docker Mongo数据库主主同步配置方法

    一.背景 不多说,请看第一篇<Docker Mongo数据库主从同步配置方法> 二.具体操作方法 1.创建目录,如创建~/test/mongo_sr1和-/test/mongo_sr2两个 ...

  8. Docker Mysql数据库双主同步配置方法

    一.背景 可先查看第一篇<Docker Mysql数据库主从同步配置方法>介绍 二.具体操作 1.创建目录(~/test/mysql_test1): --mysql --mone --da ...

  9. java实现同步的方法

    为何要实现同步 java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),      将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前 ...

随机推荐

  1. 2018-2019-2 网络对抗技术 20165303 Exp4 恶意代码分析

    实践目标 1.1是监控你自己系统的运行状态,看有没有可疑的程序在运行. 1.2是分析一个恶意软件,就分析Exp2或Exp3中生成后门软件:分析工具尽量使用原生指令或sysinternals,systr ...

  2. 用Python实现支持向量机并处理Iris数据集

    SVM全称是Support Vector Machine,即支持向量机,是一种监督式学习算法.它主要应用于分类问题,通过改进代码也可以用作回归.所谓支持向量就是距离分隔面最近的向量.支持向量机就是要确 ...

  3. iframe之间的postMessage传参

    1.传参 function IframeClose() { var obj = {method: "iframeClose"}; window.parent.postMessage ...

  4. Notepad++编译和运行C语言 (GCC)

    我们在学习C语言的时候,实际上只需要编译器和编辑器就能开搞了.(初学者过早接触IDE不利于理解程序构建的过程) 在看这篇文章的时候,假设你已经知道如何把GCC配置到环境变量,并且会在命令行/终端下使用 ...

  5. Linq语句jion on后指定多个条件

    代码如下: private void FrmMain_Load(object sender, EventArgs e) { var list1 = Supplier.GetSuppliers(); v ...

  6. PAT 1144 The Missing Number

    1144 The Missing Number (20 分)   Given N integers, you are supposed to find the smallest positive in ...

  7. CRM 员工创建并分配用户

    REPORT zjp_emp_upload. TABLES: sscrfields. DATA:gt_excel_data TYPE TABLE OF zalsmex_tabline, gs_exce ...

  8. Scala映射与元组篇

    *Scala有十分易用的语法来创建.查询和便利映射 *你需要从可变的和不可变的映射中做出选择 *默认情况下,你得到的是一个哈希映射,不过你也可以指明要树形映射 *你可以很容易地在Scala映射和Jav ...

  9. 利用itext将html页面转成pdf(不模糊)

    1.maven项目进入依赖 <dependency> <groupId>org.xhtmlrenderer</groupId> <artifactId> ...

  10. PDF 补丁丁 0.6.0.3427 版发布(修复提取图片问题)

    新的版本进一步改善了导出图片的问题.