首页IT科技互斥锁用法详解(Go 快速入门指南 – 互斥锁和定时器)

互斥锁用法详解(Go 快速入门指南 – 互斥锁和定时器)

时间2025-08-01 13:09:33分类IT科技浏览4942
导读:互斥锁...

互斥锁

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

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

错误的并发操作

先来看一个错误的示例        。

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

packagemain import"sync" funcmain(){ m:=make(map[int]bool) varwgsync.WaitGroup forj:=0;j<100;j++{ wg.Add(1) gofunc(keyint){ deferfunc(){ wg.Done() }() m[key]=true//对Map进行并发写入 }(j) } wg.Wait() } //$gorunmain.go //输出如下               ,报错信息 /** fatalerror:concurrentmapwrites fatalerror:concurrentmapwrites goroutine104[running]: main.main.func1(0x0?) /home/codes/Go-examples-for-beginners/main.go:18+0x66 createdbymain.main /home/codes/Go-examples-for-beginners/main.go:13+0x45 goroutine1[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 全部处于等待状态                     ,这样就把并发写入变成了串行写入       , 从而消除了报错问题                      。

packagemain import( "fmt" "sync" ) funcmain(){ varmusync.Mutex m:=make(map[int]bool) varwgsync.WaitGroup forj:=0;j<100;j++{ wg.Add(1) gofunc(keyint){ deferfunc(){ wg.Done() }() mu.Lock()//写入前加锁 m[key]=true//对Map进行并发写入 mu.Unlock()//写入完成解锁 }(j) } wg.Wait() fmt.Printf("Mapsize=%d\n",len(m)) } //$gorunmain.go //输出如下 /** Mapsize=100 */

超时控制

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

例子

packagemain import( "fmt" "time" ) funcmain(){ ch:=make(chanbool) gofunc(){ deferfunc(){ ch<-true }() time.Sleep(2*time.Second)//模拟超时操作 }() select{ case<-ch: fmt.Println("ok") case<-time.After(time.Second): fmt.Println("timeout!") } } //$gorunmain.go //输出如下 /** timeout! */

定时器

调用time.NewTicker方法即可。

例子

packagemain import( "fmt" "time" ) funcmain(){ ticker:=time.NewTicker(time.Second) deferticker.Stop() done:=make(chanbool) gofunc(){ 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-0215:04:05")) } } } //$gorunmain.go //输出如下        ,你的输出可能和这里的不一样 /** 2021-01-0315:40:21 2021-01-0315:40:22 2021-01-0315:40:23 2021-01-0315:40:24 2021-01-0315:40:25 Done! */

扩展阅读

1.互斥锁 - 维基百科 (https://zh.wikipedia.org/wiki/互斥锁)

2.临界区 - 百度百科 (https://baike.baidu.com/item/临界区/8942134)

联系我

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
人工智能前沿技术与发展趋势(人工智能前沿——深度学习热门领域(确定选题及研究方向)) 虚拟主机的概念(虚拟主机的缺点有哪些)