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 和 MYSQL
Oct 09 PHP
菜鸟学PHP之Smarty入门
Jan 04 PHP
Discuz! 5.0.0论坛程序中加入一段js代码,让会员点击下载附件前自动弹出提示窗口
Apr 18 PHP
php 获取远程网页内容的函数
Sep 08 PHP
PHP 裁剪图片成固定大小代码方法
Sep 09 PHP
php循环语句 for()与foreach()用法区别介绍
Sep 05 PHP
php流量统计功能的实现代码
Sep 29 PHP
PHP实现HTML生成PDF文件的方法
Nov 07 PHP
php 截取GBK文档某个位置开始的n个字符方法
Mar 08 PHP
PHP PDOStatement::getAttribute讲解
Feb 01 PHP
laravel框架实现去掉URL中index.php的方法
Oct 12 PHP
laravel 如何实现引入自己的函数或类库
Oct 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
isset和empty的区别
2007/01/15 PHP
编写漂亮的代码 - 将后台程序与前端程序分开
2008/04/23 PHP
ThinkPHP之getField详解
2014/06/20 PHP
php简单实现快速排序的方法
2015/04/04 PHP
PHP 返回13位时间戳的实现代码
2016/05/13 PHP
XHProf报告字段含义的解析
2016/05/17 PHP
PHP以json或xml格式返回请求数据的方法
2018/05/31 PHP
浅析PHP中的 inet_pton 网络函数
2019/12/16 PHP
PHP常用字符串函数用法实例总结
2020/06/04 PHP
HTML5如何适配 iPhone IOS 底部黑条
2021/03/09 HTML / CSS
公共js在页面底部加载的注意事项介绍
2013/07/18 Javascript
JS实现黑色风格的网页TAB选项卡效果代码
2015/10/09 Javascript
AngularJS 实现按需异步加载实例代码
2015/10/18 Javascript
BootStrap框架个人总结(bootstrap框架、导航条、下拉菜单、轮播广告carousel、栅格系统布局、标签页tabs、模态框、菜单定位)
2016/12/01 Javascript
js实现首屏延迟加载实现方法 js实现多屏单张图片延迟加载效果
2017/07/17 Javascript
antd组件Upload实现自己上传的实现示例
2018/12/18 Javascript
Python正则获取、过滤或者替换HTML标签的方法
2016/01/28 Python
python实现汽车管理系统
2018/11/30 Python
flask session组件的使用示例
2018/12/25 Python
Python3之不使用第三方变量,实现交换两个变量的值
2019/06/26 Python
Python使用type动态创建类操作示例
2020/02/29 Python
python GUI库图形界面开发之PyQt5滚动条控件QScrollBar详细使用方法与实例
2020/03/06 Python
使用Keras画神经网络准确性图教程
2020/06/15 Python
Python enumerate() 函数如何实现索引功能
2020/06/29 Python
python如何支持并发方法详解
2020/07/25 Python
python3中编码获取网页的实例方法
2020/11/16 Python
Ancheer官方户外和运动商店:销售电动自行车
2019/08/07 全球购物
当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
2014/09/09 面试题
生日寿宴答谢词
2014/01/19 职场文书
有趣的广告词
2014/03/18 职场文书
捐款倡议书
2014/04/14 职场文书
2014年医院个人工作总结
2014/12/09 职场文书
2014小学语文教学工作总结
2014/12/17 职场文书
初中军训感言
2015/08/01 职场文书
nginx配置限速限流基于内置模块
2022/05/02 Servers
Mysql中mvcc各场景理解应用
2022/08/05 MySQL