gf框架之gmlock - 内存锁模块

栏目: Go · 发布时间: 7年前

内容简介:文章来源:内存锁。该模块包含两个对象特性:该示例中,模拟了同时开启

文章来源: http://gf.johng.cn/os/gmlock/...

内存锁。该模块包含两个对象特性:

  1. Locker 内存锁,支持按照 给定键名生成内存锁 ,并支持 Try*Lock锁过期 特性;
  2. Mutex 对标准库底层 sync.Mutex 的封装,增加了 Try*Lock 特性;

使用方式:

import "gitee.com/johng/gf/g/os/gmlock"

使用场景:

  1. 任何需要并发安全的场景,可以替代 sync.Mutex
  2. 需要使用 Try*Lock 的场景(不需要阻塞等待锁释放);
  3. 需要 动态创建互斥锁 ,或者需要 维护大量动态锁 的场景;

方法列表

func Lock(key string, expire ...int)
func RLock(key string, expire ...int)
func RUnlock(key string)
func TryLock(key string, expire ...int) bool
func TryRLock(key string, expire ...int) bool
func Unlock(key string)
type Locker
    func New() *Locker
    func (l *Locker) Lock(key string, expire ...int)
    func (l *Locker) RLock(key string, expire ...int)
    func (l *Locker) RUnlock(key string)
    func (l *Locker) TryLock(key string, expire ...int) bool
    func (l *Locker) TryRLock(key string, expire ...int) bool
    func (l *Locker) Unlock(key string)
type Mutex
    func NewMutex() *Mutex
    func (l *Mutex) Lock()
    func (l *Mutex) RLock()
    func (l *Mutex) RUnlock()
    func (l *Mutex) TryLock() bool
    func (l *Mutex) TryRLock() bool
    func (l *Mutex) Unlock()

示例1,基本使用

package main

import (
    "time"
    "sync"
    "gitee.com/johng/gf/g/os/glog"
    "gitee.com/johng/gf/g/os/gmlock"
)

func main() {
    key := "lock"
    wg  := sync.WaitGroup{}
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(i int) {
            gmlock.Lock(key)
            glog.Println(i)
            time.Sleep(time.Second)
            gmlock.Unlock(key)
            wg.Done()
        }(i)
    }
    wg.Wait()
}

该示例中,模拟了同时开启 10 个goroutine,但同一时刻只能有一个goroutine获得锁,获得锁的goroutine执行1秒后退出,其他goroutine才能获得锁。

执行后,输出结果为:

2018-10-15 23:57:28.295 9
2018-10-15 23:57:29.296 0
2018-10-15 23:57:30.296 1
2018-10-15 23:57:31.296 2
2018-10-15 23:57:32.296 3
2018-10-15 23:57:33.297 4
2018-10-15 23:57:34.297 5
2018-10-15 23:57:35.297 6
2018-10-15 23:57:36.298 7
2018-10-15 23:57:37.298 8

示例2,过期控制

我们将以上的示例使用过期时间控制来实现。

package main

import (
    "sync"
    "gitee.com/johng/gf/g/os/glog"
    "gitee.com/johng/gf/g/os/gmlock"
)

func main() {
    key := "lock"
    wg  := sync.WaitGroup{}
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(i int) {
            gmlock.Lock(key, 1000)
            glog.Println(i)
            wg.Done()
        }(i)
    }
    wg.Wait()
}

执行后,输出结果为:

2018-10-15 23:59:14.663 9
2018-10-15 23:59:15.663 4
2018-10-15 23:59:16.663 0
2018-10-15 23:59:17.664 1
2018-10-15 23:59:18.664 2
2018-10-15 23:59:19.664 3
2018-10-15 23:59:20.664 6
2018-10-15 23:59:21.664 5
2018-10-15 23:59:22.665 7
2018-10-15 23:59:23.665 8

示例3,TryLock非阻塞锁

TryLock 方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回 true ;如果获取失败(即锁已被其他goroutine获取),则返回 false

package main

import (
    "sync"
    "gitee.com/johng/gf/g/os/glog"
    "time"
    "gitee.com/johng/gf/g/os/gmlock"
)

func main() {
    key := "lock"
    wg  := sync.WaitGroup{}
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(i int) {
            if gmlock.TryLock(key) {
                glog.Println(i)
                time.Sleep(time.Second)
                gmlock.Unlock(key)
            } else {
                glog.Println(false)
            }
            wg.Done()
        }(i)
    }
    wg.Wait()
}

同理,在该示例中,同时也只有 1 个goroutine能获得锁,其他goroutine在 TryLock 失败便直接退出了。

执行后,输出结果为:

2018-10-16 00:01:59.172 9
2018-10-16 00:01:59.172 false
2018-10-16 00:01:59.172 false
2018-10-16 00:01:59.172 false
2018-10-16 00:01:59.172 false
2018-10-16 00:01:59.172 false
2018-10-16 00:01:59.172 false
2018-10-16 00:01:59.172 false
2018-10-16 00:01:59.172 false
2018-10-16 00:01:59.176 false

示例4,多个锁机制冲突

该示例用来演示在复杂逻辑下的锁机制处理情况。

package main

import (
    "gitee.com/johng/gf/g/os/gmlock"
    "time"
    "gitee.com/johng/gf/g/os/glog"
    "fmt"
)

// 内存锁 - 手动Unlock与计时Unlock冲突校验
func main() {
    key := "key"

    // 第一次锁带时间
    gmlock.Lock(key, 1000)
    glog.Println("lock1")
    // 这个时候上一次的计时解锁已失效
    gmlock.Unlock(key)
    glog.Println("unlock1")

    fmt.Println()

    // 第二次锁,不带时间,且在执行过程中前一个Lock的定时解锁生效
    gmlock.Lock(key)
    glog.Println("lock2")
    go func() {
        // 正常情况下3秒后才能执行这句
        gmlock.Lock(key)
        glog.Println("lock by goroutine")
    }()
    time.Sleep(3*time.Second)
    // 这时再解锁
    gmlock.Unlock(key)
    // 注意3秒之后才会执行这一句
    glog.Println("unlock2")

    // 阻塞进程
    select{}
}

执行后,输出结果为:

2018-10-16 00:03:40.277 lock1
2018-10-16 00:03:40.279 unlock1

2018-10-16 00:03:40.279 lock2
2018-10-16 00:03:43.279 unlock2
2018-10-16 00:03:43.279 lock by goroutine

示例5,多文件并发写的安全控制

glog 模块写日志文件的时候有这么一个核心方法,我们拿来看一下(源代码位于 /g/os/glog/glog_logger.go )。

// 这里的写锁保证同一时刻只会写入一行日志,防止串日志的情况
func (l *Logger) print(std io.Writer, s string) {
    // 优先使用自定义的IO输出
    if l.printHeader.Val() {
        s = l.format(s)
    }
    writer := l.GetWriter()
    if writer == nil {
        // 如果设置的writer为空,那么其次判断是否有文件输出设置
        // 内部使用了内存锁,保证在glog中对同一个日志文件的并发写入不会串日志(并发安全)
        if f := l.getFilePointer(); f != nil {
            defer f.Close()
            key := l.path.Val()
            gmlock.Lock(key)
            _, err := io.WriteString(f, s)
            gmlock.Unlock(key)
            if err != nil {
                fmt.Fprintln(os.Stderr, err.Error())
            }
        }
    } else {
        l.doStdLockPrint(writer, s)
    }
    // 是否允许输出到标准输出
    if l.alsoStdPrint.Val() {
        l.doStdLockPrint(std, s)
    }
}

其中的:

gmlock.Lock(key)
...
gmlock.Unlock(key)

便使用到了内存锁的特性,其中的变量 key 表示的是日志文件的 绝对路径 ,当多个goroutine对同一个日志文件进行写入时,由 gmlock.Lock(key) 来保证对该文件的并发安全写操作。


以上所述就是小编给大家介绍的《gf框架之gmlock - 内存锁模块》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

《10%创业家》

《10%创业家》

[美] 帕特里克•J.麦金尼斯 / 李文远 / 广东人民出版社 / 2017-4 / 45.00

还在打工和创业之间苦苦挣扎吗?麦金尼斯用亲身经历告诉你,不用辞职,只需投入10%的时间和资源,就能获得100%的财务自由。你不需要雄厚的资本,也不必占用工作时间,只要准确掌握本书所授的方法,就能立即开始创业。 麦金尼斯是世界银行风投顾问,同时也是一名10%创业家。在本书中,他结合自身的创业咨询经历,为读者讲解了移动互联时代的5种创业模式,还提供了创业基因测试、10%创业计划、自传模板等个性化......一起来看看 《《10%创业家》》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具