mirror of
https://gitee.com/johng/gf
synced 2026-06-27 09:47:19 +08:00
增加gmap.Map对象;改进gfilepool文件指针池设计;改进gfile文本内容写入,增加指针池使用
This commit is contained in:
@ -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()
|
||||
}
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
|
||||
|
||||
@ -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")
|
||||
}
|
||||
Reference in New Issue
Block a user