golang线程安全的map实现

 更新时间:2019年03月11日 10:11:13   作者:hackssssss  
这篇文章主要介绍了golang线程安全的map实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

网上找的协程安全的map都是用互斥锁或者读写锁实现的,这里用单个协程来实现下,即所有的增删查改操作都集成到一个goroutine中,这样肯定不会出现多线程并发访问的问题。

基本思路是后台启动一个长期运行的goroutine,阻塞的接受自己channel中的请求req,req分为不同的请求,比如读key,写key等,然后在这个goroutine中进行各种操作。

例: Get方法向readSig(channel)中发送一条请求。请求是readReq的指针,当run方法接收到信号时,读取底层map,将值写入readReq的value中(value是个channel),Get方法阻塞的接收value,接收到就返回value。

ps:花了两个多小时写完,只是简单的做了测试,没有深入测试,另外性能也没有测过,以后有空会深入测试一下正确性以及相比加锁的写法其性能如何。

package util
 
type smap struct {
 m      map[interface{}]interface{}
 readSig   chan *readReq
 writeSig   chan *writeReq
 lenSig    chan *lenReq
 terminateSig chan bool
 delSig    chan *delReq
 scanSig   chan *scanReq
}
 
type readReq struct {
 key  interface{}
 value interface{}
 ok  chan bool
}
 
type writeReq struct {
 key  interface{}
 value interface{}
 ok  chan bool
}
 
type lenReq struct {
 len chan int
}
 
type delReq struct {
 key interface{}
 ok chan bool
}
 
type scanReq struct {
 do     func(interface{}, interface{})
 doWithBreak func(interface{}, interface{}) bool
 brea    int
 done    chan bool
}
// NewSmap returns an instance of the pointer of safemap
func NewSmap() *smap {
 var mp smap
 mp.m = make(map[interface{}]interface{})
 mp.readSig = make(chan *readReq)
 mp.writeSig = make(chan *writeReq)
 mp.lenSig = make(chan *lenReq)
 mp.delSig = make(chan *delReq)
 mp.scanSig = make(chan *scanReq)
 go mp.run()
 return &mp
}
 
//background function to operate map in one goroutine
//this can ensure that the map is Concurrent security.
func (s *smap) run() {
 for {
 select {
 case read := <-s.readSig:
  if value, ok := s.m[read.key]; ok {
  read.value = value
  read.ok <- true
  } else {
  read.ok <- false
  }
 case write := <-s.writeSig:
  s.m[write.key] = write.value
  write.ok <- true
 case l := <-s.lenSig:
  l.len <- len(s.m)
 case sc := <-s.scanSig:
  if sc.brea == 0 {
  for k, v := range s.m {
   sc.do(k, v)
  }
  } else {
  for k, v := range s.m {
   ret := sc.doWithBreak(k, v)
   if ret {
   break
   }
  }
  }
  sc.done <- true
 case d := <-s.delSig:
  delete(s.m, d.key)
  d.ok <- true
 case <-s.terminateSig:
  return
 }
 }
}
 
//Get returns the value of key which provided.
//if the key not found in map, ok will be false.
func (s *smap) Get(key interface{}) (interface{}, bool) {
 req := &readReq{
 key: key,
 ok: make(chan bool),
 }
 s.readSig <- req
 ok := <-req.ok
 return req.value, ok
}
 
//Set set the key and value to map
//ok returns true indicates that key and value is successfully added to map
func (s *smap) Set(key interface{}, value interface{}) bool {
 req := &writeReq{
 key:  key,
 value: value,
 ok:  make(chan bool),
 }
 s.writeSig <- req
 return <-req.ok //TODO 暂时先是同步的,异步的可能存在使用方面的问题。
}
 
//Clear clears all the key and value in map.
func (s *smap) Clear() {
 s.m = make(map[interface{}]interface{})
}
 
//Size returns the size of map.
func (s *smap) Size() int {
 req := &lenReq{
 len: make(chan int),
 }
 s.lenSig <- req
 return <-req.len
}
 
//terminate s.Run function. this function is usually called for debug.
//after this do NOT use smap again, because it can make your program block.
func (s *smap) TerminateBackGoroutine() {
 s.terminateSig <- true
}
 
//Del delete the key in map
func (s *smap) Del(key interface{}) bool {
 req := &delReq{
 key: key,
 ok: make(chan bool),
 }
 s.delSig <- req
 return <-req.ok
}
 
//scan the map. do is a function which operate all of the key and value in map
func (s *smap) EachItem(do func(interface{}, interface{})) {
 req := &scanReq{
 do:  do,
 brea: 0,
 done: make(chan bool),
 }
 s.scanSig <- req
 <-req.done
}
 
//scan the map util function 'do' returns true. do is a function which operate all of the key and value in map
func (s *smap) EachItemBreak(do func(interface{}, interface{}) bool, condition bool) {
 req := &scanReq{
 doWithBreak: do,
 brea:    1,
 done:    make(chan bool),
 }
 s.scanSig <- req
 <-req.done
}
 
//Exists checks whether the key which provided is exists in map
func (s *smap) Exists(key interface{}) bool {
 if _,found := s.Get(key); found {
 return true
 }
 return false
}

github地址:https://github.com/hackssssss/safemap

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • go-zero创建RESTful API 服务的方法

    go-zero创建RESTful API 服务的方法

    文章介绍了如何使用go-zero框架和goctl工具快速创建RESTfulAPI服务,通过定义.api文件并使用goctl命令,可以自动生成项目结构、路由、请求和响应模型以及处理逻辑,感兴趣的朋友一起看看吧
    2024-11-11
  • gin框架中使用JWT的定义需求及解析

    gin框架中使用JWT的定义需求及解析

    这篇文章主要为介绍了gin框架中使用JWT的定义需求及解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • 详解minio分布式文件存储

    详解minio分布式文件存储

    MinIO 是一款基于 Go 语言的高性能、可扩展、云原生支持、操作简单、开源的分布式对象存储产品,这篇文章主要介绍了minio分布式文件存储,需要的朋友可以参考下
    2023-10-10
  • 详解Go中defer与return的执行顺序

    详解Go中defer与return的执行顺序

    Go defer中改变return的值会生效吗,这就设计到了GO语言中defer与return哪个先执行的问题了,下面小编就通过简单的示例来和大家讲讲吧
    2023-07-07
  • GO语言类型转换和类型断言实例分析

    GO语言类型转换和类型断言实例分析

    这篇文章主要介绍了GO语言类型转换和类型断言,以实例形式详细分析了类型转换和类型断言的概念与使用技巧,需要的朋友可以参考下
    2015-01-01
  • Go语言共享内存读写实例分析

    Go语言共享内存读写实例分析

    这篇文章主要介绍了Go语言共享内存读写方法,实例分析了共享内存的原理与读写技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-02-02
  • golang如何通过viper读取config.yaml文件

    golang如何通过viper读取config.yaml文件

    这篇文章主要介绍了golang通过viper读取config.yaml文件,围绕golang读取config.yaml文件的相关资料展开详细内容,需要的小伙伴可以参考一下
    2022-03-03
  • GO Cobra Termui库开发终端命令行小工具轻松上手

    GO Cobra Termui库开发终端命令行小工具轻松上手

    这篇文章主要为大家介绍了GO语言开发终端命令行小工具,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • Golang开发库的集合及作用说明

    Golang开发库的集合及作用说明

    这篇文章主要为大家介绍了Golang开发golang库的集合及简单的作用说明,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2021-11-11
  • 优雅使用GoFrame共享变量Context示例详解

    优雅使用GoFrame共享变量Context示例详解

    这篇文章主要为大家介绍了优雅使用GoFrame共享变量Context示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06

最新评论