首页 > 其他分享 >Go 快速入门指南 - 互斥锁和定时器

Go 快速入门指南 - 互斥锁和定时器

时间:2022-12-24 18:32:28浏览次数:38  
标签:Map 定时器 sync 互斥 func time Go main go

互斥锁

对于任一共享资源,同一时间保证只有一个操作者,这种方法称为 ​​互斥机制​​。

关键字 ​​Mutex​​​ 表示互斥锁类型,它的 ​​Lock​​​ 方法用于获取锁,​​Unlock​​​ 方法用于释放锁。在 ​​Lock​​​ 和 ​​Unlock​​​ 之间的代码,可以读取和修改共享资源,这部分区域称为 ​​临界区​​。

错误的并发操作

先来看一个错误的示例。

在 Map 小节中讲到, ​Map​​ 不是并发安全的, 也就是说,如果在多个线程中,同时对一个 Map 进行读写,会报错。现在来验证一下, 通过启动 ​​100 个 goroutine​​ 来模拟并发调用,每个 goroutine 都对 Map 的 key 进行设置。

package main

import "sync"

func main() {
m := make(map[int]bool)

var wg sync.WaitGroup

for j := 0; j < 100; j++ {
wg.Add(1)

go func(key int) {
defer func() {
wg.Done()
}()

m[key] = true // 对 Map 进行并发写入
}(j)
}

wg.Wait()
}

// $ go run main.go
// 输出如下,报错信息
/**
fatal error: concurrent map writes
fatal error: concurrent map writes

goroutine 104 [running]:
main.main.func1(0x0?)
/home/codes/Go-examples-for-beginners/main.go:18 +0x66
created by main.main
/home/codes/Go-examples-for-beginners/main.go:13 +0x45

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc0000112c0?)
/usr/local/go/src/runtime/sema.go:62 +0x25
sync.(*WaitGroup).Wait(0x60?)
/usr/local/go/src/sync/waitgroup.go:139 +0x52
main.main()
/home/codes/Go-examples-for-beginners/main.go:22 +0x105

...
...
...
*/

通过输出信息 ​​fatal error: concurrent map writes​​ 可以看到,并发写入 Map 确实会报错。

正确的并发操作

Map 并发写入如何正确地实现呢?

一种简单的方案是在并发临界区域 (也就是设置 Map key 的地方) 进行加互斥锁操作, 互斥锁保证了同一时刻 只有一个 goroutine 获得锁,其他 goroutine 全部处于等待状态,这样就把并发写入变成了串行写入, 从而消除了报错问题。

package main

import (
"fmt"
"sync"
)

func main() {
var mu sync.Mutex
m := make(map[int]bool)

var wg sync.WaitGroup

for j := 0; j < 100; j++ {
wg.Add(1)

go func(key int) {
defer func() {
wg.Done()
}()

mu.Lock() // 写入前加锁
m[key] = true // 对 Map 进行并发写入
mu.Unlock() // 写入完成解锁
}(j)
}

wg.Wait()

fmt.Printf("Map size = %d\n", len(m))
}

// $ go run main.go
// 输出如下
/**
Map size = 100
*/

超时控制

利用 ​​channel (通道)​​​ 和 ​​time.After()​​ 方法实现超时控制。

例子

package main

import (
"fmt"
"time"
)

func main() {
ch := make(chan bool)

go func() {
defer func() {
ch <- true
}()

time.Sleep(2 * time.Second) // 模拟超时操作
}()

select {
case <-ch:
fmt.Println("ok")
case <-time.After(time.Second):
fmt.Println("timeout!")
}
}

// $ go run main.go
// 输出如下
/**
timeout!
*/

定时器

调用 ​​time.NewTicker​​ 方法即可。

例子

package main

import (
"fmt"
"time"
)

func main() {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()

done := make(chan bool)
go func() {
time.Sleep(5 * time.Second) // 模拟耗时操作
done <- true
}()

for {
select {
case <-done:
fmt.Println("Done!")
return
case <-ticker.C:
fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
}
}
}

// $ go run main.go
// 输出如下,你的输出可能和这里的不一样
/**
2021-01-03 15:40:21
2021-01-03 15:40:22
2021-01-03 15:40:23
2021-01-03 15:40:24
2021-01-03 15:40:25
Done!
*/

扩展阅读

  1. 1. 互斥锁 - 维基百科 (https://zh.wikipedia.org/wiki/%E4%BA%92%E6%96%A5%E9%94%81)
  2. 2. 临界区 - 百度百科 (https://baike.baidu.com/item/%E4%B8%B4%E7%95%8C%E5%8C%BA/8942134)

Go 快速入门指南 - 互斥锁和定时器_Go

标签:Map,定时器,sync,互斥,func,time,Go,main,go
From: https://blog.51cto.com/u_15915979/5967445

相关文章

  • django中间件
    django中间件相当于django框架的保安,只要数据过来必须经过中间件的校验。django中间件默认有7个。可以在setings内MIDDLEWARE里查看看着是字符串其实底层就是导模块的......
  • Gogs.02.构建本地仓库并上传到远程仓库
    01.通过VSCode构建一个Vue项目,npminitvue@latest配置参数并安装相关依赖  安装完成后,试运行一下,可以看到已还跑起来了; 02.本地仓构......
  • 使用PicGo+阿里云OSS实现md文档图片上传
    使用PicGo+阿里云OSS实现md文档图片上传这次给大家带来的是PicG0+阿里云Oss+typora的图床环境搭建,帮助大家提高工作效率+写博客速度!1.typora安装给大家一个链接:typora,打......
  • django中间件了解的三个方法、基于Django的插拔式设计、cookie与session简介、基于Dja
    目录Django中间件三个了解的方法1.process_view2.process_exception3.process_template_response基于Django中间实现功能的插拔式设计模拟编写一个消息通知功能(微信、qq、......
  • Go
    Chapter01编译器是全文翻译,解释器是实时翻译。1.环境搭建1.1MAC&Linux下载编译器:MAC:在golang.google.cn中点击DownloadGo,选择ApplemacOS即可,默认安装路径......
  • 1005.Django自定义过滤器及标签
    一、关于自定义自定义的引入内置函数--------->自定义函数内置模块--------->自定义模板内置过滤器------>自定义过滤器内置标签--------->自定义标签二、文件路径配......
  • 在Dubbo-go中使用TLS加密进行安全通信
    1背景Dubbo-go在Getty/Triple/Grpc三个通信层面支持TLS链路安全通信。2原理2.1证书机制:ps:可以先提前了解非对称加密机制。CA(CertificationAuthority)负责生成根证书......
  • Django
    第1章1.自己开发web框架web种类:第一种,帮你把下面所有的事都做了。Tornado:做了所有的事。第二种,不办你做socket的事,再的都帮你做了。wsgiref:做socket服务......
  • 1004.Django模板标签
    一、常用标签模板标签标签在渲染的过程中提供任意的逻辑。这个定义是刻意模糊的。例如,一个标签可以输出内容,作为控制结构,例如“if”语句或“for”循环从数据库中提取内......
  • 框架第十一课---django中间件三个了解的方法,基于django中间件实现功能的插拔式设计,coo
    昨日内容回顾forms组件渲染标签form_obj=MyForm()方式1:form_obj.as_p\form_obj.as_ul\form_obj.as_table方式2:form_obj.username.labelform_obj.username方......