Go 快速入门指南 - 互斥锁和定时器
2022/12/29 4:24:01
本文主要是介绍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! */
这篇关于Go 快速入门指南 - 互斥锁和定时器的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-15SendGrid 的 Go 客户端库怎么实现同时向多个邮箱发送邮件?-icode9专业技术文章分享
- 2024-11-15SendGrid 的 Go 客户端库怎么设置header 和 标签tag 呢?-icode9专业技术文章分享
- 2024-11-12Cargo deny安装指路
- 2024-11-02MongoDB项目实战:从入门到初级应用
- 2024-11-01随时随地一键转录,Google Cloud 新模型 Chirp 2 让语音识别更上一层楼
- 2024-10-25Google Cloud动手实验详解:如何在Cloud Run上开发无服务器应用
- 2024-10-24AI ?先驱齐聚 BAAI 2024,发布大规模语言、多模态、具身、生物计算以及 FlagOpen 2.0 等 AI 模型创新成果。
- 2024-10-20goland工具下,如修改一个项目的标准库SDK的版本-icode9专业技术文章分享
- 2024-10-17Go学习:初学者的简单教程
- 2024-10-17Go学习:新手入门完全指南