PHP 文件锁与进程锁的使用示例


Posted in PHP onAugust 07, 2017

鉴于前面介绍了swoole,就借用swoole的服务器/客户端与多进程机制对锁进行说明.

这里只针对PHP的锁机制进行说明,由于SQL的锁与其作用方式和应用场景不同,将作另行说明.

1.文件锁

  • flock()
  • fclose()
  • swoole_lock()

文件锁的可能应用场景为:

1.限制并发多进程或多台服务器需要对同一文件进行访问和修改;

2.对参与文件I/O的进程队列化和人为阻塞;

3.在业务逻辑中对文件内容进行守护;

下面是文件锁C/S通讯机制下的使用,已经省略了具体的通讯过程

Server(服务器通讯过程已略):

//监听数据发送事件
$serv->on('receive', function ($serv, $fd, $from_id, $data) {
  $serv->send($fd, "ServerEnd");

  $p_file = "locktest.txt";
  var_dump(file_get_contents($p_file));
});

Client1(服务器通讯过程已略):

$s_recv = "ww";

$p_file = "locktest.txt";

$o_file = fopen($p_file,'w+');
// flock()加锁方式:
flock($o_file,LOCK_EX);

// // swoole加锁方式:
// $lock = new swoole_lock(SWOOLE_FILELOCK, $p_file);
// $lock->lock();

fwrite($o_file, 'ss' . $s_recv);

sleep(30);
// 两种解锁方式
// flock($o_file, LOCK_UN);
// $lock->unlock();

Client2(服务器通讯过程已略):

$s_recv = "xx";

$p_file = "locktest.txt";

$o_file = fopen($p_file,'w+');
// flock()加锁方式:
flock($o_file,LOCK_EX);

// // swoole加锁方式:
// $lock = new swoole_lock(SWOOLE_FILELOCK, $p_file);
// $lock->lock();


fwrite($o_file, 'ss' . $s_recv);

// 两种解锁方式
// flock($o_file, LOCK_UN);
// $lock->unlock();

结果:

Client2被阻塞了30s,直到Client1执行结束才对文件进行了一次写入;

[l0.16@4 m29.5% c30s04] $ php swoole_client2.php

需要注意的是:

1.无论是flock()还是swoole提供的swoole_lock(),都有在进程结束时自动解锁的机制,所以在demo中即使不进行手动解锁也能正常运行,因此这里在第一个Client中执行了sleep()暂停函数来观察文件锁的效果;

2.flock()的标准释放方式为flock($file,LOCK_UN);, 但是个人喜欢fclose(),永绝后患;

2.进程锁

与文件锁不同的是,进程锁并不用于阻止对文件的I/O,而是用于防止多进程并发造成的预期之外的后果.所以需要在多进程并发时将其队列化,即在某进程的关键逻辑执行结束前阻塞其他并发进程的逻辑执行.

实现思路有几种:

1.利用flock()文件锁,创建一个临时lock文件,使用LOCK_NB模拟阻塞或非阻塞流,再在进程内部使用判定条件控制逻辑执行;

非阻塞模型demo:

$p_file = "locktest.txt";
$o_file = fopen($p_file, 'w+');

// 如果临时文件被锁定,这里的flock()将返回false
if (!flock($o_file, LOCK_EX + LOCK_NB)) {
  var_dump('Process Locked');
}
else {
  // 非阻塞模型必须在flock()中增加LOCK_NB参数
  // 当然,这里取消LOCK_NB参数就是阻塞模型了
  flock($o_file, LOCK_EX + LOCK_NB);
  var_dump('Process Locking');
  // 模拟长时间的执行操作
  sleep(10);
}

2.利用swoole提供的共享内存,缓存方法或通信方法在不同的进程中传递一个全局变量,进程获取该变量的状态后使用判定条件控制逻辑执行;

传递变量的方法很多,这里只提供一个思路,就以memcached为例;

阻塞模型demo:

// 初始化memcached
$memcached = new Memcache;
$memcached->connect("localhost", 11211);

// 获取用来做状态判定的全局变量
$s_flag = $memcached->get("flag");

if (!$s_flag) {
  // 这里利用了memcached的过期时间作为演示,实际上业务处理完成后销毁该变量即可
  $memcached->set("flag", "locked", 0, 10);
  main();
}
else {
  // 阻塞模型
  while ($s_flag == 'locked') {
    var_dump('Process locked, retrying...');
    // 设置重试时间, 避免过于频繁的操作尝试
    sleep(1);
    // 更新状态变量
    $s_flag = $memcached->get("flag");
  }
  // // 非阻塞模型
  // if ($s_flag == 'locked') {
  //   var_dump('Process locked, suspended');
  //   die();
  // }
  main();
}

// 模拟业务主函数
function main() {
  var_dump('Process Running');
  // 业务执行结束后回收memcached
  // $memcached->delete("flag");
}

这里需要注意的是:

1.memcached的过期时间不可少于程序运行的实际时间,因此建议稍微长一点,逻辑执行结束后进行回收;

2.在非阻塞模型中,若状态被判定为false,应该将进程中止或block,避免业务逻辑的继续执行;

3.在实际应用中,设置一个重试时间很有必要,这样可以很大程度上减少针对memcached的大量I/O并发,减轻服务器压力;

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
十天学会php(1)
Oct 09 PHP
PHP Zip解压 文件在线解压缩的函数代码
May 26 PHP
php做下载文件的实现代码及文件名中乱码解决方法
Feb 03 PHP
is_uploaded_file函数引发的不能上传文件问题
Oct 29 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(六)
Jun 23 PHP
PHP中IP地址与整型数字互相转换详解
Aug 20 PHP
php简单实现快速排序的方法
Apr 04 PHP
PHP SPL标准库之接口(Interface)详解
May 11 PHP
PHP Streams(流)详细介绍及使用
May 12 PHP
Adnroid 微信内置浏览器清除缓存
Jul 11 PHP
php微信公众平台交互与接口详解
Nov 28 PHP
PHP实现微信支付(jsapi支付)流程步骤详解
Mar 15 PHP
PHP实现找出有序数组中绝对值最小的数算法分析
Aug 07 #PHP
php基于session锁防止阻塞请求的方法分析
Aug 07 #PHP
在Yii2特定页面如何禁用调试工具栏Debug Toolbar详解
Aug 07 #PHP
PHP编程中的Session阻塞问题与解决方法分析
Aug 07 #PHP
PHP基于IMAP收取邮件的方法示例
Aug 07 #PHP
PHP与JavaScript针对Cookie的读写、交互操作方法详解
Aug 07 #PHP
php+javascript实现的动态显示服务器运行程序进度条功能示例
Aug 07 #PHP
You might like
用PHP实现Ftp用户的在线管理的代码
2007/03/06 PHP
跟我学Laravel之请求(Request)的生命周期
2014/10/15 PHP
PHP速成大法
2015/01/30 PHP
PHP中的闭包(匿名函数)浅析
2015/02/07 PHP
php array_pop 删除数组最后一个元素实例
2016/11/02 PHP
PHP操作MySQL中BLOB字段的方法示例【存储文本与图片】
2017/09/15 PHP
基于jquery完美拖拽,可返回拖动轨迹
2012/03/29 Javascript
jQuery使用数组编写图片无缝向左滚动
2012/12/11 Javascript
javascript中实现兼容JAVA的hashCode算法代码分享
2020/08/11 Javascript
JS小游戏之象棋暗棋源码详解
2014/09/25 Javascript
编写高性能Javascript代码的N条建议
2015/10/12 Javascript
JS控制静态页面之间传递参数获取参数并应用的简单实例
2016/08/10 Javascript
Vue 固定头 固定列 点击表头可排序的表格组件
2016/11/25 Javascript
javascript中递归的两种写法
2017/01/17 Javascript
详解Vue 方法与事件处理器
2017/06/20 Javascript
对vue里函数的调用顺序介绍
2018/03/17 Javascript
nodejs 如何手动实现服务器
2018/08/20 NodeJs
Vue跨域请求问题解决方案过程解析
2020/08/07 Javascript
vue-cli3配置favicon.ico和title的流程
2020/10/27 Javascript
详解Vue2的diff算法
2021/01/06 Vue.js
在Python中关于中文编码问题的处理建议
2015/04/08 Python
Python实现栈的方法
2015/05/26 Python
Python中遇到的小问题及解决方法汇总
2017/01/11 Python
Python装饰器原理与用法分析
2018/04/30 Python
python中sys.argv函数精简概括
2018/07/08 Python
基于TensorFlow常量、序列以及随机值生成实例
2020/01/04 Python
如何基于Python + requests实现发送HTTP请求
2020/01/13 Python
从零开始的TensorFlow+VScode开发环境搭建的步骤(图文)
2020/08/31 Python
一篇文章带你搞定Ubuntu中打开Pycharm总是卡顿崩溃
2020/11/02 Python
加拿大票务网站:Ticketmaster加拿大
2017/07/17 全球购物
市场拓展计划书
2014/05/03 职场文书
公司会议策划方案
2014/05/17 职场文书
党的群众路线教育实践活动个人对照检查材料(教师)
2014/11/04 职场文书
投标邀请书范本
2015/02/02 职场文书
男方家长婚礼答谢词
2015/09/29 职场文书
慰问信(范文3篇)
2019/10/23 职场文书