diff --git a/g/container/gmap/gmap_bench_test.go b/g/container/gmap/gmap_bench_safe_test.go similarity index 91% rename from g/container/gmap/gmap_bench_test.go rename to g/container/gmap/gmap_bench_safe_test.go index f62856e06..866bade86 100644 --- a/g/container/gmap/gmap_bench_test.go +++ b/g/container/gmap/gmap_bench_safe_test.go @@ -23,7 +23,6 @@ var sbm = NewStringBoolMap() var sim = NewStringIntMap() var sifm = NewStringInterfaceMap() var ssm = NewStringStringMap() -var uifm = NewUintInterfaceMap() // 写入性能测试 @@ -81,11 +80,6 @@ func Benchmark_StringStringMap_Set(b *testing.B) { } } -func Benchmark_UintInterfaceMap_Set(b *testing.B) { - for i := 0; i < b.N; i++ { - uifm.Set(uint(i), i) - } -} // 读取性能测试 @@ -143,9 +137,3 @@ func Benchmark_StringStringMap_Get(b *testing.B) { } } -func Benchmark_UintInterfaceMap_Get(b *testing.B) { - for i := 0; i < b.N; i++ { - uifm.Get(uint(i)) - } -} - diff --git a/g/container/gmap/gmap_syncmap_test.go b/g/container/gmap/gmap_bench_syncmap_test.go similarity index 96% rename from g/container/gmap/gmap_syncmap_test.go rename to g/container/gmap/gmap_bench_syncmap_test.go index 6db373955..061cf42b2 100644 --- a/g/container/gmap/gmap_syncmap_test.go +++ b/g/container/gmap/gmap_bench_syncmap_test.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://gitee.com/johng/gf. -// go test *.go -bench=".*" +// go test *.go -bench=".*" -benchmem package gmap_test diff --git a/g/container/gmap/gmap_bench_unsafe_test.go b/g/container/gmap/gmap_bench_unsafe_test.go new file mode 100644 index 000000000..c0eb1e164 --- /dev/null +++ b/g/container/gmap/gmap_bench_unsafe_test.go @@ -0,0 +1,139 @@ +// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://gitee.com/johng/gf. + +// go test *.go -bench=".*" -benchmem + +package gmap + +import ( + "testing" + "strconv" +) + + +var ibmUnsafe = NewIntBoolMap(false) +var iimUnsafe = NewIntIntMap(false) +var iifmUnsafe = NewIntInterfaceMap(false) +var ismUnsafe = NewIntStringMap(false) +var ififmUnsafe = NewInterfaceInterfaceMap(false) +var sbmUnsafe = NewStringBoolMap(false) +var simUnsafe = NewStringIntMap(false) +var sifmUnsafe = NewStringInterfaceMap(false) +var ssmUnsafe = NewStringStringMap(false) + +// 写入性能测试 + +func Benchmark_Unsafe_IntBoolMap_Set(b *testing.B) { + for i := 0; i < b.N; i++ { + ibmUnsafe.Set(i, true) + } +} + +func Benchmark_Unsafe_IntIntMap_Set(b *testing.B) { + for i := 0; i < b.N; i++ { + iimUnsafe.Set(i, i) + } +} + +func Benchmark_Unsafe_IntInterfaceMap_Set(b *testing.B) { + for i := 0; i < b.N; i++ { + iifmUnsafe.Set(i, i) + } +} + +func Benchmark_Unsafe_IntStringMap_Set(b *testing.B) { + for i := 0; i < b.N; i++ { + ismUnsafe.Set(i, strconv.Itoa(i)) + } +} + +func Benchmark_Unsafe_InterfaceInterfaceMap_Set(b *testing.B) { + for i := 0; i < b.N; i++ { + ififmUnsafe.Set(i, i) + } +} + +func Benchmark_Unsafe_StringBoolMap_Set(b *testing.B) { + for i := 0; i < b.N; i++ { + sbmUnsafe.Set(strconv.Itoa(i), true) + } +} + +func Benchmark_Unsafe_StringIntMap_Set(b *testing.B) { + for i := 0; i < b.N; i++ { + simUnsafe.Set(strconv.Itoa(i), i) + } +} + +func Benchmark_Unsafe_StringInterfaceMap_Set(b *testing.B) { + for i := 0; i < b.N; i++ { + sifmUnsafe.Set(strconv.Itoa(i), i) + } +} + +func Benchmark_Unsafe_StringStringMap_Set(b *testing.B) { + for i := 0; i < b.N; i++ { + ssmUnsafe.Set(strconv.Itoa(i), strconv.Itoa(i)) + } +} + + +// 读取性能测试 + +func Benchmark_Unsafe_IntBoolMap_Get(b *testing.B) { + for i := 0; i < b.N; i++ { + ibmUnsafe.Get(i) + } +} + +func Benchmark_Unsafe_IntIntMap_Get(b *testing.B) { + for i := 0; i < b.N; i++ { + iimUnsafe.Get(i) + } +} + +func Benchmark_Unsafe_IntInterfaceMap_Get(b *testing.B) { + for i := 0; i < b.N; i++ { + iifmUnsafe.Get(i) + } +} + +func Benchmark_Unsafe_IntStringMap_Get(b *testing.B) { + for i := 0; i < b.N; i++ { + ismUnsafe.Get(i) + } +} + +func Benchmark_Unsafe_InterfaceInterfaceMap_Get(b *testing.B) { + for i := 0; i < b.N; i++ { + ififmUnsafe.Get(i) + } +} + +func Benchmark_Unsafe_StringBoolMap_Get(b *testing.B) { + for i := 0; i < b.N; i++ { + sbmUnsafe.Get(strconv.Itoa(i)) + } +} + +func Benchmark_Unsafe_StringIntMap_Get(b *testing.B) { + for i := 0; i < b.N; i++ { + simUnsafe.Get(strconv.Itoa(i)) + } +} + +func Benchmark_Unsafe_StringInterfaceMap_Get(b *testing.B) { + for i := 0; i < b.N; i++ { + sifmUnsafe.Get(strconv.Itoa(i)) + } +} + +func Benchmark_Unsafe_StringStringMap_Get(b *testing.B) { + for i := 0; i < b.N; i++ { + ssmUnsafe.Get(strconv.Itoa(i)) + } +} + diff --git a/g/container/gmap/int_bool_map.go b/g/container/gmap/gmap_int_bool_map.go similarity index 65% rename from g/container/gmap/int_bool_map.go rename to g/container/gmap/gmap_int_bool_map.go index 9cbf62741..a1e7cb5bc 100644 --- a/g/container/gmap/int_bool_map.go +++ b/g/container/gmap/gmap_int_bool_map.go @@ -69,23 +69,70 @@ func (this *IntBoolMap) Get(key int) bool { return val } -// 获取键值,如果键值不存在则写入默认值 -func (this *IntBoolMap) GetWithDefault(key int, value bool) bool { +// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。 +// 在高并发下有用,防止数据写入的并发逻辑错误。 +func (this *IntBoolMap) doSetWithLockCheck(key int, value bool) bool { this.mu.Lock() - val, ok := this.m[key] - if !ok { - this.m[key] = value - val = value + if v, ok := this.m[key]; ok { + this.mu.Unlock() + return v } + this.m[key] = value this.mu.Unlock() - return val + return value } -// 删除键值对 -func (this *IntBoolMap) Remove(key int) { - this.mu.Lock() - delete(this.m, key) - this.mu.Unlock() +// 当键名存在时返回其键值,否则写入指定的键值 +func (this *IntBoolMap) GetOrSet(key int, value bool) bool { + this.mu.RLock() + v, ok := this.m[key] + this.mu.RUnlock() + if !ok { + return this.doSetWithLockCheck(key, value) + } else { + return v + } +} + +// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成 +func (this *IntBoolMap) GetOrSetFunc(key int, f func() bool) bool { + this.mu.RLock() + v, ok := this.m[key] + this.mu.RUnlock() + if !ok { + return this.doSetWithLockCheck(key, f()) + } else { + return v + } +} + +// 与GetOrSetFunc不同的是,f是在写锁机制内执行 +func (this *IntBoolMap) GetOrSetFuncLock(key int, f func() bool) bool { + this.mu.RLock() + val, ok := this.m[key] + this.mu.RUnlock() + if !ok { + this.mu.Lock() + defer this.mu.Unlock() + if v, ok := this.m[key]; ok { + this.mu.Unlock() + return v + } + val = f() + this.m[key] = val + return val + } else { + return val + } +} + +// 当键名不存在时写入,并返回true;否则返回false。 +func (this *IntBoolMap) SetIfNotExist(key int, value bool) bool { + if !this.Contains(key) { + this.doSetWithLockCheck(key, value) + return true + } + return false } // 批量删除键值对 @@ -98,7 +145,7 @@ func (this *IntBoolMap) BatchRemove(keys []int) { } // 返回对应的键值,并删除该键值 -func (this *IntBoolMap) GetAndRemove(key int) (bool) { +func (this *IntBoolMap) Remove(key int) bool { this.mu.Lock() val, exists := this.m[key] if exists { diff --git a/g/container/gmap/int_int_map.go b/g/container/gmap/gmap_int_int_map.go similarity index 65% rename from g/container/gmap/int_int_map.go rename to g/container/gmap/gmap_int_int_map.go index c4f037ec2..cb0c3c394 100644 --- a/g/container/gmap/int_int_map.go +++ b/g/container/gmap/gmap_int_int_map.go @@ -69,23 +69,70 @@ func (this *IntIntMap) Get(key int) (int) { return val } -// 获取键值,如果键值不存在则写入默认值 -func (this *IntIntMap) GetWithDefault(key int, value int) int { +// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。 +// 在高并发下有用,防止数据写入的并发逻辑错误。 +func (this *IntIntMap) doSetWithLockCheck(key int, value int) int { this.mu.Lock() - val, ok := this.m[key] - if !ok { - this.m[key] = value - val = value + if v, ok := this.m[key]; ok { + this.mu.Unlock() + return v } + this.m[key] = value this.mu.Unlock() - return val + return value } -// 删除键值对 -func (this *IntIntMap) Remove(key int) { - this.mu.Lock() - delete(this.m, key) - this.mu.Unlock() +// 当键名存在时返回其键值,否则写入指定的键值 +func (this *IntIntMap) GetOrSet(key int, value int) int { + this.mu.RLock() + v, ok := this.m[key] + this.mu.RUnlock() + if !ok { + return this.doSetWithLockCheck(key, value) + } else { + return v + } +} + +// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成 +func (this *IntIntMap) GetOrSetFunc(key int, f func() int) int { + this.mu.RLock() + v, ok := this.m[key] + this.mu.RUnlock() + if !ok { + return this.doSetWithLockCheck(key, f()) + } else { + return v + } +} + +// 与GetOrSetFunc不同的是,f是在写锁机制内执行 +func (this *IntIntMap) GetOrSetFuncLock(key int, f func() int) int { + this.mu.RLock() + val, ok := this.m[key] + this.mu.RUnlock() + if !ok { + this.mu.Lock() + defer this.mu.Unlock() + if v, ok := this.m[key]; ok { + this.mu.Unlock() + return v + } + val = f() + this.m[key] = val + return val + } else { + return val + } +} + +// 当键名不存在时写入,并返回true;否则返回false。 +func (this *IntIntMap) SetIfNotExist(key int, value int) bool { + if !this.Contains(key) { + this.doSetWithLockCheck(key, value) + return true + } + return false } // 批量删除键值对 @@ -98,7 +145,7 @@ func (this *IntIntMap) BatchRemove(keys []int) { } // 返回对应的键值,并删除该键值 -func (this *IntIntMap) GetAndRemove(key int) (int) { +func (this *IntIntMap) Remove(key int) int { this.mu.Lock() val, exists := this.m[key] if exists { diff --git a/g/container/gmap/int_interface_map.go b/g/container/gmap/gmap_int_interface_map.go similarity index 66% rename from g/container/gmap/int_interface_map.go rename to g/container/gmap/gmap_int_interface_map.go index 9a6c3a738..a75d14ff2 100644 --- a/g/container/gmap/int_interface_map.go +++ b/g/container/gmap/gmap_int_interface_map.go @@ -67,23 +67,58 @@ func (this *IntInterfaceMap) Get(key int) (interface{}) { return val } -// 获取键值,如果键值不存在则写入默认值 -func (this *IntInterfaceMap) GetWithDefault(key int, value interface{}) interface{} { +// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。 +// 在高并发下有用,防止数据写入的并发逻辑错误。 +func (this *IntInterfaceMap) doSetWithLockCheck(key int, value interface{}) interface{} { this.mu.Lock() - val, ok := this.m[key] - if !ok { - this.m[key] = value - val = value + defer this.mu.Unlock() + if v, ok := this.m[key]; ok { + return v } - this.mu.Unlock() - return val + if f, ok := value.(func() interface {}); ok { + value = f() + } + this.m[key] = value + return value } -// 删除键值对 -func (this *IntInterfaceMap) Remove(key int) { - this.mu.Lock() - delete(this.m, key) - this.mu.Unlock() +// 当键名存在时返回其键值,否则写入指定的键值 +func (this *IntInterfaceMap) GetOrSet(key int, value interface{}) interface{} { + if v := this.Get(key); v == nil { + return this.doSetWithLockCheck(key, value) + } else { + return v + } +} + +// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成 +func (this *IntInterfaceMap) GetOrSetFunc(key int, f func() interface{}) interface{} { + if v := this.Get(key); v == nil { + v = f() + this.doSetWithLockCheck(key, v) + return v + } else { + return v + } +} + +// 与GetOrSetFunc不同的是,f是在写锁机制内执行 +func (this *IntInterfaceMap) GetOrSetFuncLock(key int, f func() interface{}) interface{} { + if v := this.Get(key); v == nil { + this.doSetWithLockCheck(key, f) + return v + } else { + return v + } +} + +// 当键名不存在时写入,并返回true;否则返回false。 +func (this *IntInterfaceMap) SetIfNotExist(key int, value interface{}) bool { + if !this.Contains(key) { + this.doSetWithLockCheck(key, value) + return true + } + return false } // 批量删除键值对 @@ -96,7 +131,7 @@ func (this *IntInterfaceMap) BatchRemove(keys []int) { } // 返回对应的键值,并删除该键值 -func (this *IntInterfaceMap) GetAndRemove(key int) (interface{}) { +func (this *IntInterfaceMap) Remove(key int) interface{} { this.mu.Lock() val, exists := this.m[key] if exists { diff --git a/g/container/gmap/int_string_map.go b/g/container/gmap/gmap_int_string_map.go similarity index 65% rename from g/container/gmap/int_string_map.go rename to g/container/gmap/gmap_int_string_map.go index 294937a46..876d6264f 100644 --- a/g/container/gmap/int_string_map.go +++ b/g/container/gmap/gmap_int_string_map.go @@ -69,23 +69,70 @@ func (this *IntStringMap) Get(key int) string { return val } -// 获取键值,如果键值不存在则写入默认值 -func (this *IntStringMap) GetWithDefault(key int, value string) string { +// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。 +// 在高并发下有用,防止数据写入的并发逻辑错误。 +func (this *IntStringMap) doSetWithLockCheck(key int, value string) string { this.mu.Lock() - val, ok := this.m[key] - if !ok { - this.m[key] = value - val = value + if v, ok := this.m[key]; ok { + this.mu.Unlock() + return v } + this.m[key] = value this.mu.Unlock() - return val + return value } -// 删除键值对 -func (this *IntStringMap) Remove(key int) { - this.mu.Lock() - delete(this.m, key) - this.mu.Unlock() +// 当键名存在时返回其键值,否则写入指定的键值 +func (this *IntStringMap) GetOrSet(key int, value string) string { + this.mu.RLock() + v, ok := this.m[key] + this.mu.RUnlock() + if !ok { + return this.doSetWithLockCheck(key, value) + } else { + return v + } +} + +// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成 +func (this *IntStringMap) GetOrSetFunc(key int, f func() string) string { + this.mu.RLock() + v, ok := this.m[key] + this.mu.RUnlock() + if !ok { + return this.doSetWithLockCheck(key, f()) + } else { + return v + } +} + +// 与GetOrSetFunc不同的是,f是在写锁机制内执行 +func (this *IntStringMap) GetOrSetFuncLock(key int, f func() string) string { + this.mu.RLock() + val, ok := this.m[key] + this.mu.RUnlock() + if !ok { + this.mu.Lock() + defer this.mu.Unlock() + if v, ok := this.m[key]; ok { + this.mu.Unlock() + return v + } + val = f() + this.m[key] = val + return val + } else { + return val + } +} + +// 当键名不存在时写入,并返回true;否则返回false。 +func (this *IntStringMap) SetIfNotExist(key int, value string) bool { + if !this.Contains(key) { + this.doSetWithLockCheck(key, value) + return true + } + return false } // 批量删除键值对 @@ -98,7 +145,7 @@ func (this *IntStringMap) BatchRemove(keys []int) { } // 返回对应的键值,并删除该键值 -func (this *IntStringMap) GetAndRemove(key int) (string) { +func (this *IntStringMap) Remove(key int) string { this.mu.Lock() val, exists := this.m[key] if exists { diff --git a/g/container/gmap/interface_interface_map.go b/g/container/gmap/gmap_interface_interface_map.go similarity index 95% rename from g/container/gmap/interface_interface_map.go rename to g/container/gmap/gmap_interface_interface_map.go index 32a05a048..2cf371c04 100644 --- a/g/container/gmap/interface_interface_map.go +++ b/g/container/gmap/gmap_interface_interface_map.go @@ -73,15 +73,14 @@ func (this *InterfaceInterfaceMap) Get(key interface{}) interface{} { // 在高并发下有用,防止数据写入的并发逻辑错误。 func (this *InterfaceInterfaceMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} { this.mu.Lock() + defer this.mu.Unlock() if v, ok := this.m[key]; ok { - this.mu.Unlock() return v } if f, ok := value.(func() interface {}); ok { value = f() } this.m[key] = value - this.mu.Unlock() return value } @@ -124,13 +123,6 @@ func (this *InterfaceInterfaceMap) SetIfNotExist(key interface{}, value interfac return false } -// 删除键值对 -func (this *InterfaceInterfaceMap) Remove(key interface{}) { - this.mu.Lock() - delete(this.m, key) - this.mu.Unlock() -} - // 批量删除键值对 func (this *InterfaceInterfaceMap) BatchRemove(keys []interface{}) { this.mu.Lock() @@ -141,7 +133,7 @@ func (this *InterfaceInterfaceMap) BatchRemove(keys []interface{}) { } // 返回对应的键值,并删除该键值 -func (this *InterfaceInterfaceMap) GetAndRemove(key interface{}) (interface{}) { +func (this *InterfaceInterfaceMap) Remove(key interface{}) interface{} { this.mu.Lock() val, exists := this.m[key] if exists { diff --git a/g/container/gmap/string_bool_map.go b/g/container/gmap/gmap_string_bool_map.go similarity index 67% rename from g/container/gmap/string_bool_map.go rename to g/container/gmap/gmap_string_bool_map.go index e95d7f9a3..371dae76e 100644 --- a/g/container/gmap/string_bool_map.go +++ b/g/container/gmap/gmap_string_bool_map.go @@ -69,23 +69,70 @@ func (this *StringBoolMap) Get(key string) bool { return val } -// 获取键值,如果键值不存在则写入默认值 -func (this *StringBoolMap) GetWithDefault(key string, value bool) bool { +// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。 +// 在高并发下有用,防止数据写入的并发逻辑错误。 +func (this *StringBoolMap) doSetWithLockCheck(key string, value bool) bool { this.mu.Lock() - val, ok := this.m[key] - if !ok { - this.m[key] = value - val = value + if v, ok := this.m[key]; ok { + this.mu.Unlock() + return v } + this.m[key] = value this.mu.Unlock() - return val + return value } -// 删除键值对 -func (this *StringBoolMap) Remove(key string) { - this.mu.Lock() - delete(this.m, key) - this.mu.Unlock() +// 当键名存在时返回其键值,否则写入指定的键值 +func (this *StringBoolMap) GetOrSet(key string, value bool) bool { + this.mu.RLock() + v, ok := this.m[key] + this.mu.RUnlock() + if !ok { + return this.doSetWithLockCheck(key, value) + } else { + return v + } +} + +// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成 +func (this *StringBoolMap) GetOrSetFunc(key string, f func() bool) bool { + this.mu.RLock() + v, ok := this.m[key] + this.mu.RUnlock() + if !ok { + return this.doSetWithLockCheck(key, f()) + } else { + return v + } +} + +// 与GetOrSetFunc不同的是,f是在写锁机制内执行 +func (this *StringBoolMap) GetOrSetFuncLock(key string, f func() bool) bool { + this.mu.RLock() + val, ok := this.m[key] + this.mu.RUnlock() + if !ok { + this.mu.Lock() + defer this.mu.Unlock() + if v, ok := this.m[key]; ok { + this.mu.Unlock() + return v + } + val = f() + this.m[key] = val + return val + } else { + return val + } +} + +// 当键名不存在时写入,并返回true;否则返回false。 +func (this *StringBoolMap) SetIfNotExist(key string, value bool) bool { + if !this.Contains(key) { + this.doSetWithLockCheck(key, value) + return true + } + return false } // 批量删除键值对 @@ -98,7 +145,7 @@ func (this *StringBoolMap) BatchRemove(keys []string) { } // 返回对应的键值,并删除该键值 -func (this *StringBoolMap) GetAndRemove(key string) (bool) { +func (this *StringBoolMap) Remove(key string) bool { this.mu.Lock() val, exists := this.m[key] if exists { diff --git a/g/container/gmap/string_int_map.go b/g/container/gmap/gmap_string_int_map.go similarity index 65% rename from g/container/gmap/string_int_map.go rename to g/container/gmap/gmap_string_int_map.go index 4711eddee..9afac1bac 100644 --- a/g/container/gmap/string_int_map.go +++ b/g/container/gmap/gmap_string_int_map.go @@ -67,23 +67,70 @@ func (this *StringIntMap) Get(key string) int { return val } -// 获取键值,如果键值不存在则写入默认值 -func (this *StringIntMap) GetWithDefault(key string, value int) int { +// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。 +// 在高并发下有用,防止数据写入的并发逻辑错误。 +func (this *StringIntMap) doSetWithLockCheck(key string, value int) int { this.mu.Lock() - val, ok := this.m[key] - if !ok { - this.m[key] = value - val = value + if v, ok := this.m[key]; ok { + this.mu.Unlock() + return v } + this.m[key] = value this.mu.Unlock() - return val + return value } -// 删除键值对 -func (this *StringIntMap) Remove(key string) { - this.mu.Lock() - delete(this.m, key) - this.mu.Unlock() +// 当键名存在时返回其键值,否则写入指定的键值 +func (this *StringIntMap) GetOrSet(key string, value int) int { + this.mu.RLock() + v, ok := this.m[key] + this.mu.RUnlock() + if !ok { + return this.doSetWithLockCheck(key, value) + } else { + return v + } +} + +// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成 +func (this *StringIntMap) GetOrSetFunc(key string, f func() int) int { + this.mu.RLock() + v, ok := this.m[key] + this.mu.RUnlock() + if !ok { + return this.doSetWithLockCheck(key, f()) + } else { + return v + } +} + +// 与GetOrSetFunc不同的是,f是在写锁机制内执行 +func (this *StringIntMap) GetOrSetFuncLock(key string, f func() int) int { + this.mu.RLock() + val, ok := this.m[key] + this.mu.RUnlock() + if !ok { + this.mu.Lock() + defer this.mu.Unlock() + if v, ok := this.m[key]; ok { + this.mu.Unlock() + return v + } + val = f() + this.m[key] = val + return val + } else { + return val + } +} + +// 当键名不存在时写入,并返回true;否则返回false。 +func (this *StringIntMap) SetIfNotExist(key string, value int) bool { + if !this.Contains(key) { + this.doSetWithLockCheck(key, value) + return true + } + return false } // 批量删除键值对 @@ -96,7 +143,7 @@ func (this *StringIntMap) BatchRemove(keys []string) { } // 返回对应的键值,并删除该键值 -func (this *StringIntMap) GetAndRemove(key string) (int) { +func (this *StringIntMap) Remove(key string) int { this.mu.Lock() val, exists := this.m[key] if exists { diff --git a/g/container/gmap/string_interface_map.go b/g/container/gmap/gmap_string_interface_map.go similarity index 95% rename from g/container/gmap/string_interface_map.go rename to g/container/gmap/gmap_string_interface_map.go index 5e76e9201..d284a8f26 100644 --- a/g/container/gmap/string_interface_map.go +++ b/g/container/gmap/gmap_string_interface_map.go @@ -72,16 +72,15 @@ func (this *StringInterfaceMap) Get(key string) interface{} { // 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。 // 在高并发下有用,防止数据写入的并发逻辑错误。 func (this *StringInterfaceMap) doSetWithLockCheck(key string, value interface{}) interface{} { - this.mu.Lock() + this.mu.Lock() + defer this.mu.Unlock() if v, ok := this.m[key]; ok { - this.mu.Unlock() return v } if f, ok := value.(func() interface {}); ok { value = f() } this.m[key] = value - this.mu.Unlock() return value } @@ -124,13 +123,6 @@ func (this *StringInterfaceMap) SetIfNotExist(key string, value interface{}) boo return false } -// 删除键值对 -func (this *StringInterfaceMap) Remove(key string) { - this.mu.Lock() - delete(this.m, key) - this.mu.Unlock() -} - // 批量删除键值对 func (this *StringInterfaceMap) BatchRemove(keys []string) { this.mu.Lock() @@ -141,7 +133,7 @@ func (this *StringInterfaceMap) BatchRemove(keys []string) { } // 返回对应的键值,并删除该键值 -func (this *StringInterfaceMap) GetAndRemove(key string) interface{} { +func (this *StringInterfaceMap) Remove(key string) interface{} { this.mu.Lock() val, exists := this.m[key] if exists { diff --git a/g/container/gmap/string_string_map.go b/g/container/gmap/gmap_string_string_map.go similarity index 67% rename from g/container/gmap/string_string_map.go rename to g/container/gmap/gmap_string_string_map.go index 7ea883e26..ce24fc25d 100644 --- a/g/container/gmap/string_string_map.go +++ b/g/container/gmap/gmap_string_string_map.go @@ -67,23 +67,70 @@ func (this *StringStringMap) Get(key string) string { return val } -// 获取键值,如果键值不存在则写入默认值 -func (this *StringStringMap) GetWithDefault(key string, value string) string { +// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。 +// 在高并发下有用,防止数据写入的并发逻辑错误。 +func (this *StringStringMap) doSetWithLockCheck(key string, value string) string { this.mu.Lock() - val, ok := this.m[key] - if !ok { - this.m[key] = value - val = value + if v, ok := this.m[key]; ok { + this.mu.Unlock() + return v } + this.m[key] = value this.mu.Unlock() - return val + return value } -// 删除键值对 -func (this *StringStringMap) Remove(key string) { - this.mu.Lock() - delete(this.m, key) - this.mu.Unlock() +// 当键名存在时返回其键值,否则写入指定的键值 +func (this *StringStringMap) GetOrSet(key string, value string) string { + this.mu.RLock() + v, ok := this.m[key] + this.mu.RUnlock() + if !ok { + return this.doSetWithLockCheck(key, value) + } else { + return v + } +} + +// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成 +func (this *StringStringMap) GetOrSetFunc(key string, f func() string) string { + this.mu.RLock() + v, ok := this.m[key] + this.mu.RUnlock() + if !ok { + return this.doSetWithLockCheck(key, f()) + } else { + return v + } +} + +// 与GetOrSetFunc不同的是,f是在写锁机制内执行 +func (this *StringStringMap) GetOrSetFuncLock(key string, f func() string) string { + this.mu.RLock() + val, ok := this.m[key] + this.mu.RUnlock() + if !ok { + this.mu.Lock() + defer this.mu.Unlock() + if v, ok := this.m[key]; ok { + this.mu.Unlock() + return v + } + val = f() + this.m[key] = val + return val + } else { + return val + } +} + +// 当键名不存在时写入,并返回true;否则返回false。 +func (this *StringStringMap) SetIfNotExist(key string, value string) bool { + if !this.Contains(key) { + this.doSetWithLockCheck(key, value) + return true + } + return false } // 批量删除键值对 @@ -96,7 +143,7 @@ func (this *StringStringMap) BatchRemove(keys []string) { } // 返回对应的键值,并删除该键值 -func (this *StringStringMap) GetAndRemove(key string) string { +func (this *StringStringMap) Remove(key string) string { this.mu.Lock() val, exists := this.m[key] if exists { diff --git a/g/container/gmap/uint_interface_map.go b/g/container/gmap/uint_interface_map.go deleted file mode 100644 index 8a6f8db62..000000000 --- a/g/container/gmap/uint_interface_map.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://gitee.com/johng/gf. -// - -package gmap - -import ( - "gitee.com/johng/gf/g/container/internal/rwmutex" -) - -type UintInterfaceMap struct { - mu *rwmutex.RWMutex - m map[uint]interface{} -} - -func NewUintInterfaceMap(safe...bool) *UintInterfaceMap { - return &UintInterfaceMap{ - m : make(map[uint]interface{}), - mu : rwmutex.New(safe...), - } -} - -func (this *UintInterfaceMap) Iterator(f func (k uint, v interface{}) bool) { - this.mu.RLock() - defer this.mu.RUnlock() - for k, v := range this.m { - if !f(k, v) { - break - } - } -} - -// 哈希表克隆 -func (this *UintInterfaceMap) Clone() map[uint]interface{} { - m := make(map[uint]interface{}) - this.mu.RLock() - for k, v := range this.m { - m[k] = v - } - this.mu.RUnlock() - return m -} - -// 设置键值对 -func (this *UintInterfaceMap) Set(key uint, val interface{}) { - this.mu.Lock() - this.m[key] = val - this.mu.Unlock() -} - -// 批量设置键值对 -func (this *UintInterfaceMap) BatchSet(m map[uint]interface{}) { - this.mu.Lock() - for k, v := range m { - this.m[k] = v - } - this.mu.Unlock() -} - -// 获取键值 -func (this *UintInterfaceMap) Get(key uint) interface{} { - this.mu.RLock() - val, _ := this.m[key] - this.mu.RUnlock() - return val -} - -// 获取键值,如果键值不存在则写入默认值 -func (this *UintInterfaceMap) GetWithDefault(key uint, value interface{}) interface{} { - this.mu.Lock() - val, ok := this.m[key] - if !ok { - this.m[key] = value - val = value - } - this.mu.Unlock() - return val -} - -// 删除键值对 -func (this *UintInterfaceMap) Remove(key uint) { - this.mu.Lock() - delete(this.m, key) - this.mu.Unlock() -} - -// 批量删除键值对 -func (this *UintInterfaceMap) BatchRemove(keys []uint) { - this.mu.Lock() - for _, key := range keys { - delete(this.m, key) - } - this.mu.Unlock() -} - -// 返回对应的键值,并删除该键值 -func (this *UintInterfaceMap) GetAndRemove(key uint) (interface{}) { - this.mu.Lock() - val, exists := this.m[key] - if exists { - delete(this.m, key) - } - this.mu.Unlock() - return val -} - -// 返回键列表 -func (this *UintInterfaceMap) Keys() []uint { - this.mu.RLock() - keys := make([]uint, 0) - for key, _ := range this.m { - keys = append(keys, key) - } - this.mu.RUnlock() - return keys -} - -// 返回值列表(注意是随机排序) -func (this *UintInterfaceMap) Values() []interface{} { - this.mu.RLock() - vals := make([]interface{}, 0) - for _, val := range this.m { - vals = append(vals, val) - } - this.mu.RUnlock() - return vals -} - -// 是否存在某个键 -func (this *UintInterfaceMap) Contains(key uint) bool { - this.mu.RLock() - _, exists := this.m[key] - this.mu.RUnlock() - return exists -} - -// 哈希表大小 -func (this *UintInterfaceMap) Size() int { - this.mu.RLock() - length := len(this.m) - this.mu.RUnlock() - return length -} - -// 哈希表是否为空 -func (this *UintInterfaceMap) IsEmpty() bool { - this.mu.RLock() - empty := len(this.m) == 0 - this.mu.RUnlock() - return empty -} - -// 清空哈希表 -func (this *UintInterfaceMap) Clear() { - this.mu.Lock() - this.m = make(map[uint]interface{}) - this.mu.Unlock() -} - -// 并发安全写锁操作,使用自定义方法执行加锁修改操作 -func (this *UintInterfaceMap) LockFunc(f func(m map[uint]interface{})) { - this.mu.Lock(true) - defer this.mu.Unlock(true) - f(this.m) -} - -// 并发安全读锁操作,使用自定义方法执行加锁读取操作 -func (this *UintInterfaceMap) RLockFunc(f func(m map[uint]interface{})) { - this.mu.RLock(true) - defer this.mu.RUnlock(true) - f(this.m) -} diff --git a/g/container/gset/int_set.go b/g/container/gset/gset_int_set.go similarity index 100% rename from g/container/gset/int_set.go rename to g/container/gset/gset_int_set.go diff --git a/g/container/gset/interface_set.go b/g/container/gset/gset_interface_set.go similarity index 100% rename from g/container/gset/interface_set.go rename to g/container/gset/gset_interface_set.go diff --git a/g/container/gset/string_set.go b/g/container/gset/gset_string_set.go similarity index 100% rename from g/container/gset/string_set.go rename to g/container/gset/gset_string_set.go diff --git a/g/container/gset/gset_test.go b/g/container/gset/gset_test.go index 0a4ecbc6f..f032ad968 100644 --- a/g/container/gset/gset_test.go +++ b/g/container/gset/gset_test.go @@ -15,7 +15,6 @@ import ( ) var ints = gset.NewIntSet() -var uints = gset.NewUintSet() var itfs = gset.NewInterfaceSet() var strs = gset.NewStringSet() @@ -37,24 +36,6 @@ func BenchmarkIntSet_Remove(b *testing.B) { } } -func BenchmarkUintSet_Add(b *testing.B) { - for i := 0; i < b.N; i++ { - uints.Add(uint(i)) - } -} - -func BenchmarkUintSet_Contains(b *testing.B) { - for i := 0; i < b.N; i++ { - uints.Contains(uint(i)) - } -} - -func BenchmarkUintSet_Remove(b *testing.B) { - for i := 0; i < b.N; i++ { - uints.Remove(uint(i)) - } -} - func BenchmarkInterfaceSet_Add(b *testing.B) { for i := 0; i < b.N; i++ { itfs.Add(i) diff --git a/g/os/gcache/gcache.go b/g/os/gcache/gcache.go index 5ac21c7ca..379779a7b 100644 --- a/g/os/gcache/gcache.go +++ b/g/os/gcache/gcache.go @@ -46,6 +46,11 @@ func GetOrSetFunc(key interface{}, f func() interface{}, expire int) interface{} return cache.GetOrSetFunc(key, f, expire) } +// 与GetOrSetFunc不同的是,f是在写锁机制内执行 +func GetOrSetFuncLock(key interface{}, f func() interface{}, expire int) interface{} { + return cache.GetOrSetFuncLock(key, f, expire) +} + // 是否存在指定的键名,true表示存在,false表示不存在。 func Contains(key interface{}) bool { return cache.Contains(key) @@ -57,8 +62,8 @@ func Remove(key interface{}) interface{} { } // (使用全局KV缓存对象)批量删除指定键值对 -func BatchRemove(keys []interface{}) map[interface{}]interface{} { - return cache.BatchRemove(keys) +func BatchRemove(keys []interface{}) { + cache.BatchRemove(keys) } // 获得所有的键名,组成数组返回 diff --git a/g/os/gcache/gcache_mem_cache.go b/g/os/gcache/gcache_mem_cache.go index 98da545d4..80272595a 100644 --- a/g/os/gcache/gcache_mem_cache.go +++ b/g/os/gcache/gcache_mem_cache.go @@ -213,14 +213,12 @@ func (c *memCache) Remove(key interface{}) interface{} { } // 批量删除键值对,并返回被删除的键值对数据 -func (c *memCache) BatchRemove(keys []interface{}) map[interface{}]interface{} { - m := make(map[interface{}]interface{}) +func (c *memCache) BatchRemove(keys []interface{}) { + c.dmu.Lock() for _, key := range keys { - if v := c.Remove(key); v != nil { - m[key] = v - } + delete(c.data, key) } - return m + c.dmu.Unlock() } // 获得所有的键名,组成数组返回