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 相关文章推荐
一个可以找出源代码中所有中文的工具
Oct 25 PHP
PHP基础知识介绍
Sep 17 PHP
网页上facebook分享功能具体实现
Jan 26 PHP
PHP图片自动裁切应付不同尺寸的显示
Oct 16 PHP
php计算到指定日期还有多少天的方法
Apr 14 PHP
PHP之预定义接口详解
Jul 29 PHP
PHP中文竖排转换实现方法
Oct 23 PHP
php关键字仅替换一次的实现函数
Oct 29 PHP
PHP实现的mysql操作类【MySQL与MySQLi方式】
Oct 07 PHP
在Laravel中实现使用AJAX动态刷新部分页面
Oct 15 PHP
thinkphp5 模型实例化获得数据对象的教程
Oct 18 PHP
php设计模式之正面模式实例分析【星际争霸游戏案例】
Mar 24 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获取目标函数执行时间示例
2014/03/04 PHP
Laravel如何友好的修改.env配置文件详解
2017/06/07 PHP
PHP数据库操作四:mongodb用法分析
2017/08/16 PHP
PHP清除缓存的几种方法总结
2017/09/12 PHP
PHP实现八皇后算法
2019/05/06 PHP
Javascript 去除数组的重复元素
2010/05/04 Javascript
JavaScript新窗口与子窗口传值详解
2014/02/11 Javascript
浅谈JSON中stringify 函数、toJosn函数和parse函数
2015/01/26 Javascript
JavaScript创建一个object对象并操作对象属性的用法
2015/03/23 Javascript
jQuery模拟黑客帝国矩阵效果实例
2015/06/28 Javascript
jQuery.Callbacks()回调函数队列用法详解
2016/06/14 Javascript
js实现倒计时效果(小于10补零)
2017/03/08 Javascript
Bootstrap Table使用整理(三)
2017/06/09 Javascript
JS/HTML5游戏常用算法之追踪算法实例详解
2018/12/12 Javascript
vue权限管理系统的实现代码
2019/01/17 Javascript
JS异步错误捕获的一些事小结
2019/04/26 Javascript
微信小程序实现录制、试听、上传音频功能(带波形图)
2020/02/27 Javascript
JS实现前端路由功能示例【原生路由】
2020/05/29 Javascript
jQuery实现回到顶部效果
2020/10/19 jQuery
jquery自定义组件实例详解
2020/12/31 jQuery
[26:52]LGD vs EG 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/18 DOTA
50行代码实现贪吃蛇(具体思路及代码)
2013/04/27 Python
使用Python从有道词典网页获取单词翻译
2016/07/03 Python
python 简单的多线程链接实现代码
2016/08/28 Python
基于Django框架利用Ajax实现点赞功能实例代码
2018/08/19 Python
python 检查文件mime类型的方法
2018/12/08 Python
python绘制散点图并标记序号的方法
2018/12/11 Python
python requests 库请求带有文件参数的接口实例
2019/01/03 Python
python环境下安装opencv库的方法
2020/03/05 Python
Softmax函数原理及Python实现过程解析
2020/05/22 Python
项目经理岗位职责
2013/11/11 职场文书
自主招生自荐信指南
2014/02/04 职场文书
幼儿教师师德承诺书
2014/05/23 职场文书
作弊检讨书范文
2015/05/06 职场文书
2016年记者节感言
2015/12/08 职场文书
工作违纪的检讨书范文
2019/07/09 职场文书