Любомир обнови решението на 16.11.2014 20:58 (преди над 3 години)
+package main
+
+import (
+ "strconv"
+ "strings"
+ "time"
+)
+
+type ExpireMap struct {
+ innerMap map[string]expireValue
+ expiredChan chan string
+ isDestroyed bool
+}
+
+type ExpireMapError struct {
+ errorMessage string
+}
+
+func (emr ExpireMapError) Error() string {
+ return emr.errorMessage
+}
+
+type expireValue struct {
+ value interface{}
+ expireDuration time.Duration
+ expireTime time.Time
+}
+
+func NewExpireMap() *ExpireMap {
+ e := new(ExpireMap)
+ e.innerMap = make(map[string]expireValue)
+ e.expiredChan = make(chan string)
+ return e
+}
+
+func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
+ keyValue := new(expireValue)
+ keyValue.value = value
+ keyValue.expireDuration = expire
+ keyValue.expireTime = time.Now().Add(expire)
+ em.innerMap[key] = *keyValue
+ go func(expire time.Duration) {
+ select {
+ case <-time.After(expire):
+ if em.Contains(key) {
+ go func() {
+ if em.isDestroyed {
+ return
+ }
+ em.expiredChan <- key
+ }()
+ em.Delete(key)
+ }
+ }
+ }(expire)
+}
+
+func (em *ExpireMap) Get(key string) (interface{}, bool) {
+ value, ok := em.innerMap[key]
+ return value.value, ok
+}
+
+func (em *ExpireMap) GetInt(key string) (int, bool) {
+ if val, ok := em.innerMap[key].value.(int); ok {
+ return val, ok
+ }
+ return 0, false
+}
+
+func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
+ if val, ok := em.innerMap[key].value.(float64); ok {
+ return val, ok
+ }
+ return 0, false
+}
+
+func (em *ExpireMap) GetString(key string) (string, bool) {
+ if val, ok := em.innerMap[key].value.(string); ok {
+ return val, ok
+ }
+ return "", false
+}
+
+func (em *ExpireMap) GetBool(key string) (bool, bool) {
+ if val, ok := em.innerMap[key].value.(bool); ok {
+ return val, ok
+ }
+ return false, false
+}
+
+func (em *ExpireMap) Expires(key string) (time.Time, bool) {
+ if value, ok := em.innerMap[key]; ok {
+ return value.expireTime, ok
+ }
+ return *new(time.Time), false
+}
+
+func (em *ExpireMap) Delete(key string) {
+ if em.Contains(key) {
+ delete(em.innerMap, key)
+ }
+}
+
+func (em *ExpireMap) Contains(key string) bool {
+ _, ok := em.innerMap[key]
+ return ok
+}
+
+func (em *ExpireMap) Size() int {
+ return len(em.innerMap)
+}
+
+func (em *ExpireMap) Increment(key string) error {
+ return em.changeIntValue(key, func(num int) int {
+ return num + 1
+ })
+}
+
+func (em *ExpireMap) Decrement(key string) error {
+ return em.changeIntValue(key, func(num int) int {
+ return num - 1
+ })
+}
+
+func (em *ExpireMap) changeIntValue(key string, changeFunction func(value int) int) error {
+ if num, ok := em.GetInt(key); ok {
+ tmp := em.innerMap[key]
+ tmp.value = changeFunction(num)
+ em.innerMap[key] = tmp
+ return nil
+ }
+ if str, ok := em.GetString(key); ok {
+ if numValue, ok := strconv.Atoi(str); ok == nil {
+ numValue = changeFunction(numValue)
+ tmp := em.innerMap[key]
+ tmp.value = strconv.Itoa(numValue)
+ em.innerMap[key] = tmp
+ return nil
+ }
+ }
+ return ExpireMapError{
+ errorMessage: "Error when changing integer value",
+ }
+}
+
+func (em *ExpireMap) changeStringValue(key string, changeFunction func(value string) string) error {
+ if str, ok := em.GetString(key); ok {
+ str = changeFunction(str)
+ tmp := em.innerMap[key]
+ tmp.value = str
+ em.innerMap[key] = tmp
+ return nil
+ }
+ return ExpireMapError{
+ errorMessage: "Error when changing string value",
+ }
+}
+
+func (em *ExpireMap) ToUpper(key string) error {
+ return em.changeStringValue(key, func(str string) string {
+ return strings.ToUpper(str)
+ })
+}
+
+func (em *ExpireMap) ToLower(key string) error {
+ return em.changeStringValue(key, func(str string) string {
+ return strings.ToLower(str)
+ })
+}
+
+func (em *ExpireMap) ExpiredChan() <-chan string {
+ return em.expiredChan
+}
+
+func (em *ExpireMap) Cleanup() {
+ for key, _ := range em.innerMap {
+ em.Delete(key)
+ }
+}
+
+func (em *ExpireMap) Destroy() {
+ em.Cleanup()
+ close(em.expiredChan)
+ em.isDestroyed = true
+}
Навярно още неща но това е може би най-важното :
em.Set("foo", 5, time.Millisecond)
em.Destroy()
time.Sleep(time.Second)
// panic here
В допълнение не е задължително някой да слуша на ExpiredChan-а, както и не очакваме че ако никой не слуша и изтекът 10 ключа, като почне ще получи че са изтекли - очакваме обратното.
п.п. change* функциите ми харесват и принципно има type switch но на теб май не ти трябва