在以往的项目中,遇到高并发大流量需求做并发控制的时候一般都使用redis分布式锁或者mysql加锁处理高并发情况。最近遇到一个php项目,没有安装redis,由于某种原因也不考虑使用mysql加锁控制并发,所以采用文件锁的方式控制并发,整理了下代码
php版本
class FileLock
{
/** @var string 锁名称 唯一性 */
private string $key;
/** @var string 锁文件 */
private string $file = "";
/** @var 文件资源 */
private $fp = null;
public function __construct(string $key)
{
if (empty($key)) {
throw new \InvalidArgumentException("key 不能为空");
}
$this->key = $key;
}
/**
* 加锁
*/
public function lock(): bool
{
// 文件路径
$file = "lock_{$this->key}.txt";
$this->file = $file;
$fp = fopen($file, "w+");
if (is_resource($fp)) {
$this->fp = $fp;
} else {
return false;
}
return flock($fp, LOCK_EX);
}
public function unlock(): bool
{
if (is_resource($this->fp)) {
return flock($this->fp, LOCK_UN); // 释放锁
}
return false;
}
public function __destruct()
{
if (is_resource($this->fp)) {
@flock($this->fp, LOCK_UN);
}
if (is_file($this->file)) {
@unlink($this->file);
}
}
}
go版本
同时整理go版本实现
package main
import (
"fmt"
"os"
"syscall"
"time"
)
// FileLock 实现文件锁定
type FileLock struct {
file string
f *os.File
how int
}
func (l *FileLock) How(how int) {
l.how = how
}
func New(file string) *FileLock {
return &FileLock{
file: file,
how: syscall.LOCK_EX | syscall.LOCK_NB, // 默认非阻塞
}
}
// Lock 加锁
func (l *FileLock) Lock() error {
f, err := os.Create(l.file)
if err != nil {
return err
}
l.f = f
// syscall.LOCK_NB 非阻塞
if err := syscall.Flock(int(f.Fd()), l.how); err != nil {
return err
}
return nil
}
// Unlock 释放锁
func (l *FileLock) Unlock() error {
defer l.f.Close()
if err := syscall.Flock(int(l.f.Fd()), syscall.LOCK_UN); err != nil {
// 可以增加报警 或者 直接删除文件
return err
}
return nil
}
func main() {
work()
}
func work() {
// 给该方法加锁
lockedFile := "/tmp/my_lock.lock"
flock := New(lockedFile)
//flock.How(syscall.LOCK_EX) 阻塞
err := flock.Lock()
if err != nil {
// 获取锁失败
fmt.Println(err.Error())
return
}
defer flock.Unlock()
fmt.Println("执行业务代码,模拟长时间")
time.Sleep(time.Second * 50)
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。