Ангел обнови решението на 16.11.2014 21:52 (преди над 3 години)
+package main
+
+import (
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+type ExpireMap struct {
+ mutex *sync.Mutex
+ cache map[string]interface{}
+ durations map[string]time.Time
+ channel chan string
+ goroutines int
+ quit chan struct{}
+ quitCleanup chan struct{}
+}
+
+type ExpireMapError struct {
+ message string
+}
+
+func (e *ExpireMapError) Error() string {
+ return e.message
+}
+
+func NewExpireMap() *ExpireMap {
+ var em ExpireMap
+ em.cache = make(map[string]interface{})
+ em.mutex = &sync.Mutex{}
+ em.durations = make(map[string]time.Time)
+ em.channel = make(chan string)
+ em.goroutines = 0
+ em.quit = make(chan struct{}, 1)
+ em.quitCleanup = make(chan struct{}, 1)
+ return &em
+}
+
+func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
+ em.mutex.Lock()
+
+ em.cache[key] = value
+ em.goroutines++
+
+ em.durations[key] = time.Now().Add(expire)
+ defer em.mutex.Unlock()
+
+ go func(em *ExpireMap, key string, expire time.Duration) {
+ select {
+ case <-em.quit:
+ em.mutex.Lock()
+ em.goroutines--
+ number := em.goroutines
+ em.mutex.Unlock()
+ if number == 0 {
+ em.quitCleanup <- struct{}{}
+ return
+ } else {
+ em.quit <- struct{}{}
+ return
+ }
+ case <-time.After(expire):
+ em.mutex.Lock()
+ _, ok := em.cache[key]
+ em.mutex.Unlock()
+ em.Delete(key)
+ if ok {
+ em.mutex.Lock()
+ em.goroutines++
+ em.mutex.Unlock()
+ select {
+ case em.channel <- key:
+ em.mutex.Lock()
+ em.goroutines--
+ em.mutex.Unlock()
+ return
+ case <-em.quit:
+ em.mutex.Lock()
+ em.goroutines--
+ number := em.goroutines
+ em.mutex.Unlock()
+ if number == 0 {
+ em.quitCleanup <- struct{}{}
+ } else {
+ em.quit <- struct{}{}
+ return
+ }
+ }
+ }
+ return
+ }
+ }(em, key, expire)
+}
+
+func (em *ExpireMap) Get(key string) (interface{}, bool) {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+ value, ok := em.cache[key]
+ return value, ok
+}
+
+func (em *ExpireMap) GetInt(key string) (int, bool) {
+ value, status := em.Get(key)
+
+ if value, ok := value.(int); status && ok {
+ return value, status
+ } else {
+ return 0, false
+ }
+}
+
+func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
+ value, status := em.Get(key)
+ if value, ok := value.(float64); status && ok {
+ return value, status
+ } else {
+ return 0.0, false
+ }
+}
+
+func (em *ExpireMap) GetString(key string) (string, bool) {
+ value, status := em.Get(key)
+ if value, ok := value.(string); status && ok {
+ return value, status
+ } else {
+ return "", false
+ }
+}
+
+func (em *ExpireMap) GetBool(key string) (bool, bool) {
+ value, status := em.Get(key)
+ if value, ok := value.(bool); status && ok {
+ return value, status
+ } else {
+ return false, false
+ }
+}
+
+func (em *ExpireMap) Expires(key string) (time.Time, bool) {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+ time, status := em.durations[key]
+ return time, status
+}
+
+func (em *ExpireMap) Delete(key string) {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+
+ delete(em.cache, key)
+ delete(em.durations, key)
+ em.goroutines--
+}
+
+func (em *ExpireMap) Contains(key string) bool {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+ _, ok := em.cache[key]
+ return ok
+}
+
+func (em *ExpireMap) Size() int {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+ return em.goroutines
+}
+
+func (em *ExpireMap) Increment(key string) error {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+
+ if number, ok := em.cache[key].(int); ok == true {
+ em.cache[key] = number + 1
+ return nil
+ } else if number, ok := strconv.Atoi(em.cache[key].(string)); ok == nil {
+ em.cache[key] = strconv.Itoa(number + 1)
+ return nil
+ }
+ return &ExpireMapError{"Value is not of type Int or String."}
+}
+
+func (em *ExpireMap) Decrement(key string) error {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+
+ if number, ok := em.cache[key].(int); ok == true {
+ em.cache[key] = number - 1
+ return nil
+ } else if number, ok := strconv.Atoi(em.cache[key].(string)); ok == nil {
+ em.cache[key] = strconv.Itoa(number - 1)
+ return nil
+ }
+
+ return &ExpireMapError{"Value is not of type Int or String."}
+}
+
+func (em *ExpireMap) ToUpper(key string) error {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+ if value, ok := em.cache[key].(string); ok == true {
+ em.cache[key] = strings.ToUpper(value)
+ return nil
+ } else {
+ return &ExpireMapError{"Value is not of type String."}
+ }
+}
+
+func (em *ExpireMap) ToLower(key string) error {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+ if value, ok := em.cache[key].(string); ok == true {
+ em.cache[key] = strings.ToLower(value)
+ return nil
+ } else {
+ return &ExpireMapError{"Value is not of type String."}
+ }
+}
+
+func (em *ExpireMap) ExpiredChan() <-chan string {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+ return em.channel
+}
+
+func (em *ExpireMap) Cleanup() {
+ em.quit <- struct{}{}
+ <-em.quitCleanup
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+
+ em.cache = make(map[string]interface{})
+ em.durations = make(map[string]time.Time)
+ em.channel = make(chan string)
+ em.goroutines = 0
+ em.quit = make(chan struct{}, 1)
+ em.quitCleanup = make(chan struct{})
+}
+
+func (em *ExpireMap) Destroy() {
+ em.mutex.Lock()
+ number := em.goroutines
+ em.mutex.Unlock()
+ if number != 0 {
+ em.quit <- struct{}{}
+ <-em.quitCleanup
+ }
+
+ em.cache = nil
+ em.durations = nil
+ em.goroutines = 0
+ close(em.channel)
+ close(em.quit)
+ close(em.quitCleanup)
+}
ах ... не е зле - мисля че е малко сложно ....
И в никаква последователност(виж колко е часа) моите забележки:
това на 70 ред е заради ? Ако отговора е ред 75 и 80 ... помисли пак.
Не е задължително някой да слуша на ExpiredChan-а, покрай което имаш и възможността някой да ти Destroy-не мапа без да слуша (и да ти се затвори ExpiredChan-а)
Това em.goroutines и особено ползването му за Size не ми харесва ...
ползвай defer за unlock където може ... и мисля че много си играеш с ключалките.
Можеш да минеш с един map.