golang线程安全的map

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liyunlong41/article/details/84259488

网上找的协程安全的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
}

猜你喜欢

转载自blog.csdn.net/liyunlong41/article/details/84259488
今日推荐