经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Go语言 » 查看文章
解析golang中的并发安全和锁问题
来源:jb51  时间:2021/11/8 17:18:52  对本文有异议

1. 并发安全

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. var (
  7. sum int
  8. wg sync.WaitGroup
  9. )
  10. func test() {
  11. for i := 0; i < 5000000; i++ {
  12. sum += 1
  13. }
  14. wg.Done()
  15. }
  16. func main() {
  17. // 并发和安全锁
  18. wg.Add(2)
  19. go test()
  20. go test()
  21. wg.Wait()
  22. fmt.Println(sum)
  23. }

  上面的代码中我们开启了两个goroutine去累加变量x的值,这两个goroutine在访问和修改x变量的时候就会存在数据竞争,导致最后的结果与期待的不符。

2. 互斥锁

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. var (
  7. sum int
  8. wg sync.WaitGroup
  9. mu sync.Mutex // 定义一个互斥锁
  10. )
  11. func test() {
  12. for i := 0; i < 10000000; i++ {
  13. // 互斥锁它能够保证同时只能有一个goroutine去访问共享资源
  14. mu.Lock()
  15. sum += 1
  16. mu.Unlock()
  17. }
  18. wg.Done()
  19. }
  20. func main() {
  21. fmt.Println(mu)
  22. // 并发和安全锁
  23. wg.Add(2)
  24. go test()
  25. go test()
  26. wg.Wait()
  27. fmt.Println(sum)
  28. }

  使用互斥锁能够保证同一时间有且只有一个goroutine进入临界区,其他的goroutine则在等待锁;当互斥锁释放后,等待的goroutine才可以获取锁进入临界区,多个goroutine同时等待一个锁时,唤醒的策略是随机的。

3. 读写互斥锁

互斥锁是完全互斥的,但是有很多实际的场景下是读多写少的,当我们并发的去读取一个资源不涉及资源修改的时候是没有必要加锁的,这种场景下使用读写锁是更好的一种选择。读写锁在Go语言中使用sync包中的RWMutex类型。

读写锁分为两种:读锁和写锁。当一个goroutine获取读锁之后,其他的goroutine如果是获取读锁会继续获得锁,如果是获取写锁就会等待;当一个goroutine获取写锁之后,其他的goroutine无论是获取读锁还是写锁都会等待。

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. "time"
  6. )
  7. var (
  8. x int
  9. wg sync.WaitGroup
  10. mu sync.Mutex // 定义一个互斥锁
  11. rw sync.RWMutex // 定义一个读写锁,注意:只有读多写少的时候,读写锁才能发挥其优势
  12. )
  13. func write() {
  14. rw.Lock()
  15. x += 1
  16. time.Sleep(10 * time.Millisecond) // 假设写入时间耗费10毫秒
  17. rw.Unlock()
  18. wg.Done()
  19. }
  20. func read() {
  21. rw.RLock()
  22. time.Sleep(time.Millisecond)
  23. rw.RUnlock()
  24. wg.Done()
  25. }
  26. func main() {
  27. start := time.Now()
  28. for i := 0; i < 10; i++ {
  29. wg.Add(1)
  30. go write()
  31. } // 写耗时:160毫秒左右
  32. for i := 0; i < 1000; i++ {
  33. wg.Add(1)
  34. go read()
  35. } // 读耗时:15毫秒左右
  36. wg.Wait()
  37. end := time.Now()
  38. fmt.Println("执行时间:", end.Sub(start))
  39. }

  需要注意的是读写锁非常适合读多写少的场景,如果读和写的操作差别不大,读写锁的优势就发挥不出来。

到此这篇关于golang中的并发安全和锁的文章就介绍到这了,更多相关golang并发和锁内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号