增加gmap.Map对象;改进gfilepool文件指针池设计;改进gfile文本内容写入,增加指针池使用

This commit is contained in:
john
2018-07-26 13:00:04 +08:00
parent 9efffa49e2
commit adbf6bc579
8 changed files with 164 additions and 135 deletions

View File

@ -6,3 +6,14 @@
// 并发安全的哈希MAP.
package gmap
// 默认的Map对象其实就是InterfaceInterfaceMap的别名。
// 注意
// 1、这个Map是所有并发安全Map中效率最低的如果对效率要求比较高的场合请合理选择对应数据类型的Map
// 2、这个Map的优点是使用简便由于键值都是interface{}类型,因此对键值的数据类型要求不高;
// 3、底层实现比较类似于sync.Map
type Map = InterfaceInterfaceMap
func NewMap() *Map {
return NewInterfaceInterfaceMap()
}

View File

@ -25,63 +25,127 @@ var sifm = NewStringInterfaceMap()
var ssm = NewStringStringMap()
var uifm = NewUintInterfaceMap()
func BenchmarkIntBoolMap_Set(b *testing.B) {
// 写入性能测试
func Benchmark_IntBoolMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
ibm.Set(i, true)
}
}
func BenchmarkIntIntMap_Set(b *testing.B) {
func Benchmark_IntIntMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
iim.Set(i, i)
}
}
func BenchmarkIntInterfaceMap_Set(b *testing.B) {
func Benchmark_IntInterfaceMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
iifm.Set(i, i)
}
}
func BenchmarkIntStringMap_Set(b *testing.B) {
func Benchmark_IntStringMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
ism.Set(i, strconv.Itoa(i))
}
}
func BenchmarkInterfaceInterfaceMap_Set(b *testing.B) {
func Benchmark_InterfaceInterfaceMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
ififm.Set(i, i)
}
}
func BenchmarkStringBoolMap_Set(b *testing.B) {
func Benchmark_StringBoolMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
sbm.Set(strconv.Itoa(i), true)
}
}
func BenchmarkStringIntMap_Set(b *testing.B) {
func Benchmark_StringIntMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
sim.Set(strconv.Itoa(i), i)
}
}
func BenchmarkStringInterfaceMap_Set(b *testing.B) {
func Benchmark_StringInterfaceMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
sifm.Set(strconv.Itoa(i), i)
}
}
func BenchmarkStringStringMap_Set(b *testing.B) {
func Benchmark_StringStringMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
ssm.Set(strconv.Itoa(i), strconv.Itoa(i))
}
}
func BenchmarkUintInterfaceMap_Set(b *testing.B) {
func Benchmark_UintInterfaceMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
uifm.Set(uint(i), i)
}
}
// 读取性能测试
func Benchmark_IntBoolMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
ibm.Get(i)
}
}
func Benchmark_IntIntMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
iim.Get(i)
}
}
func Benchmark_IntInterfaceMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
iifm.Get(i)
}
}
func Benchmark_IntStringMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
ism.Get(i)
}
}
func Benchmark_InterfaceInterfaceMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
ififm.Get(i)
}
}
func Benchmark_StringBoolMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
sbm.Get(strconv.Itoa(i))
}
}
func Benchmark_StringIntMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
sim.Get(strconv.Itoa(i))
}
}
func Benchmark_StringInterfaceMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
sifm.Get(strconv.Itoa(i))
}
}
func Benchmark_StringStringMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
ssm.Get(strconv.Itoa(i))
}
}
func Benchmark_UintInterfaceMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
uifm.Get(uint(i))
}
}

View File

@ -9,18 +9,21 @@ package gpool
import (
"time"
"errors"
"gitee.com/johng/gf/g/os/gtime"
"gitee.com/johng/gf/g/container/glist"
"gitee.com/johng/gf/g/container/gtype"
"errors"
"math"
)
// 对象池
type Pool struct {
list *glist.List // 可用/闲置的文件指针链表
closed *gtype.Bool // 连接池是否已关闭
Expire int64 // (毫秒)闲置最大时间,超过该时间则被系统回收
NewFunc func()(interface{}, error) // 创建对象的方法定义
list *glist.List // 可用/闲置的文件指针链表
closed *gtype.Bool // 连接池是否已关闭
Expire int64 // (毫秒)闲置最大时间,超过该时间则被系统回收
NewFunc func()(interface{}, error) // 创建对象的方法定义
ExpireFunc func(interface{}) // 对象的过期销毁方法(当池对象销毁需要执行额外的销毁操作时,需要定义该方法)
// 例如: net.Conn, os.File等对象都需要执行额外关闭操作
}
// 对象池数据项
@ -30,8 +33,12 @@ type poolItem struct {
}
// 创建一个对象池,为保证执行效率,过期时间一旦设定之后无法修改
// expire = 0表示不过期expire < 0表示使用完立即回收expire > 0表示超时回收
// 注意过期时间单位为**毫秒**
func New(expire int, newFunc...func() (interface{}, error)) *Pool {
if expire == 0 {
expire = math.MaxInt64
}
r := &Pool {
list : glist.New(),
closed : gtype.NewBool(),
@ -44,9 +51,14 @@ func New(expire int, newFunc...func() (interface{}, error)) *Pool {
return r
}
// 设置对象过期销毁时的关闭方法
func (p *Pool) SetExpireFunc(expireFunc func(interface{})) {
p.ExpireFunc = expireFunc
}
// 放一个临时对象到池中
func (p *Pool) Put(item interface{}) {
p.list.PushBack(&poolItem{
p.list.PushBack(&poolItem {
expire : gtime.Millisecond() + p.Expire,
value : item,
})
@ -85,11 +97,14 @@ func (p *Pool) expireCheckingLoop() {
for !p.closed.Val() {
for {
if r := p.list.PopFront(); r != nil {
f := r.(*poolItem)
if f.expire > gtime.Millisecond() {
p.list.PushFront(f)
item := r.(*poolItem)
if item.expire > gtime.Millisecond() {
p.list.PushFront(item)
break
}
if p.ExpireFunc != nil {
p.ExpireFunc(item.value)
}
}
}
time.Sleep(time.Second)

View File

@ -71,12 +71,12 @@ func SetCap(cap int) {
cache.cap.Set(cap)
}
// (使用全局KV缓存对象)设置kv缓存键值对过期时间单位为毫秒
// (使用全局KV缓存对象)设置kv缓存键值对过期时间单位为**毫秒**
func Set(key string, value interface{}, expire int) {
cache.Set(key, value, expire)
}
// (使用全局KV缓存对象)批量设置kv缓存键值对过期时间单位为毫秒
// (使用全局KV缓存对象)批量设置kv缓存键值对过期时间单位为**毫秒**
func BatchSet(data map[string]interface{}, expire int) {
cache.BatchSet(data, expire)
}

View File

@ -23,6 +23,7 @@ import (
"path/filepath"
"gitee.com/johng/gf/g/util/gregex"
"gitee.com/johng/gf/g/container/gtype"
"gitee.com/johng/gf/g/os/gfilepool"
)
// 封装了常用的文件操作方法如需更详细的文件控制请查看官方os包
@ -328,14 +329,13 @@ func putContents(path string, data []byte, flag int, perm os.FileMode) error {
return err
}
}
// 创建/打开文件
f, err := os.OpenFile(path, flag, perm)
// 创建/打开文件使用文件指针池默认为60秒
f, err := gfilepool.OpenWithPool(path, flag, perm, 60000)
if err != nil {
return err
}
defer f.Close()
n, err := f.Write(data)
if err != nil {
if n, err := f.Write(data); err != nil {
return err
} else if n < len(data) {
return io.ErrShortWrite

View File

@ -9,123 +9,73 @@ package gfilepool
import (
"os"
"time"
"sync"
"strconv"
"gitee.com/johng/gf/g/os/gtime"
"gitee.com/johng/gf/g/container/gmap"
"gitee.com/johng/gf/g/container/glist"
"gitee.com/johng/gf/g/container/gtype"
"gitee.com/johng/gf/g/container/gpool"
"fmt"
)
// 文件指针池
type Pool struct {
path string // 文件绝对路径
flag int // 文件打开标识
list *glist.List // 可用/闲置的文件指针链表
idle int // 闲置最大时间,超过该时间则被系统回收(秒)
closed *gtype.Bool // 连接池是否已关闭
pool *gpool.Pool // 底层对象池
}
// 文件指针池指针
type PoolItem struct {
mu sync.RWMutex
type File struct {
*os.File // 底层文件指针
mu sync.RWMutex // 互斥锁
pool *Pool // 所属池
file *os.File // 指针对象
expire *gtype.Int64 // 过期时间(秒)
}
// 全局指针池expire < 0表示不过期expire = 0表示使用完立即回收expire > 0表示超时回收
var pools = gmap.NewStringInterfaceMap()
// 获得文件对象,并自动创建指针池
func OpenWithPool(path string, flag int, expire int) (*PoolItem, error) {
key := path + strconv.Itoa(flag) + strconv.Itoa(expire)
func OpenWithPool(path string, flag int, perm os.FileMode, expire int) (*File, error) {
key := fmt.Sprintf("%s&%d&%d&%d", path, flag, expire, perm)
result := pools.Get(key)
if result != nil {
return result.(*Pool).File()
}
pool := New(path, flag, expire)
pool := New(path, flag, perm, expire)
pools.Set(key, pool)
return pool.File()
}
// 创建一个文件指针池expire < 0表示不过期expire = 0表示使用完立即回收expire > 0表示超时回收
func New(path string, flag int, expire int) *Pool {
r := &Pool {
path : path,
flag : flag,
list : glist.New(),
idle : expire,
closed : gtype.NewBool(),
}
// 独立的线程执行过期清理工作
if expire != -1 {
go func(p *Pool) {
for !p.closed.Val() {
if r := p.list.PopFront(); r != nil {
f := r.(*PoolItem)
// 必须小于中间有1秒的缓存时间防止同时获取和判断过期时冲突
if f.expire.Val() < gtime.Second() {
f.destroy()
} else {
// 重新推回去
p.list.PushFront(f)
break
}
}
time.Sleep(3 * time.Second)
}
}(r)
}
return r
// 创建一个文件指针池expire = 0表示不过期expire < 0表示使用完立即回收expire > 0表示超时回收
func New(path string, flag int, perm os.FileMode, expire int) *Pool {
p := &Pool {}
p.pool = gpool.New(expire, func() (interface{}, error) {
file, err := os.OpenFile(path, flag, perm)
if err != nil {
return nil, err
}
return &File{
File : file,
pool : p,
}, nil
})
p.pool.SetExpireFunc(func(i interface{}) {
i.(*File).File.Close()
})
return p
}
// 获得一个文件打开指针
func (p *Pool) File() (*PoolItem, error) {
if p.list.Len() > 0 {
for {
// 从队列头依次查找,返回一个未过期的指针
if r := p.list.PopFront(); r != nil {
f := r.(*PoolItem)
if f.expire.Val() > gtime.Second() {
return f, nil
} else if f.file != nil {
f.destroy()
}
} else {
break
}
}
}
file, err := os.OpenFile(p.path, p.flag, 0666)
if err != nil {
func (p *Pool) File() (*File, error) {
if v, err := p.pool.Get(); err != nil {
return nil, err
} else {
return v.(*File), nil
}
return &PoolItem {
pool : p,
file : file,
expire : gtype.NewInt64(),
}, nil
}
// 关闭指针池
func (p *Pool) Close() {
p.closed.Set(true)
p.pool.Close()
}
// 获得底层文件指针
func (f *PoolItem) File() *os.File {
return f.file
func (f *File) Close() {
f.pool.pool.Put(f)
}
// 关闭指针链接(软关闭),放回池中重复使用
func (f *PoolItem) Close() {
f.expire.Set(gtime.Second() + int64(f.pool.idle))
f.pool.list.PushBack(f)
}
// 销毁指针
func (f *PoolItem) destroy() {
f.file.Close()
}

View File

@ -57,10 +57,10 @@ func (l *Logger) GetIO() io.Writer {
}
// 获取默认的文件IO
func (l *Logger) getFileByPool() *gfilepool.PoolItem {
func (l *Logger) getFileByPool() *gfilepool.File {
if path := l.path.Val(); path != "" {
fpath := path + gfile.Separator + time.Now().Format("2006-01-02.log")
if fp, err := gfilepool.OpenWithPool(fpath, gDEFAULT_FILE_POOL_FLAGS, 86400); err == nil {
if fp, err := gfilepool.OpenWithPool(fpath, gDEFAULT_FILE_POOL_FLAGS, 0666, 86400000); err == nil {
return fp
} else {
fmt.Fprintln(os.Stderr, err)
@ -104,21 +104,22 @@ func (l *Logger) SetStdPrint(open bool) {
// 这里的写锁保证统一时刻只会写入一行日志,防止串日志的情况
func (l *Logger) print(defaultIO io.Writer, s string) {
w := l.GetIO()
if w == nil {
if v := l.getFileByPool(); v != nil {
w = v.File()
// 同时输出到文件和终端 @author zseeker
writer := l.GetIO()
if writer == nil {
if f := l.getFileByPool(); f != nil {
writer = f
// 同时输出到文件和终端
// @author zseeker
if l.stdprint.Val() {
w = io.MultiWriter(w, defaultIO)
writer = io.MultiWriter(writer, defaultIO)
}
defer v.Close()
defer f.Close()
} else {
w = defaultIO
writer = defaultIO
}
}
l.mu.Lock()
fmt.Fprint(w, l.format(s))
fmt.Fprint(writer, l.format(s))
l.mu.Unlock()
}

View File

@ -1,23 +1,11 @@
package main
import (
"fmt"
"time"
"gitee.com/johng/gf/g/os/gfile"
)
var c = make(chan int, 10)
func Chan() chan int {
fmt.Println("yes chan")
return c
}
func main() {
for {
select {
case <- Chan():
default:
time.Sleep(time.Second)
}
}
gfile.PutContentsAppend("/tmp/test", "1")
gfile.PutContentsAppend("/tmp/test", "2")
gfile.PutContentsAppend("/tmp/test", "3")
}