PHP有没有 原子性,PHP程序的原子性和PHP的文件锁
首先说明一下我的环境是 Nginx + spawn-cgi。spawn-cgi启动了64个php-cgi,端口为9000(./spawn-cgi -p 9000 -F 64 -f ~/php-cgi)
接下来假如我们有这样一个需求:每次用户输入一串数据,我们将用户的信息以及这串数据记录到文本当中。程序如下:
test.php
$usrinfo = isset($_GET["usrinfo"])?$_GET["usrinfo"]:exit(1);
$stinfo = isset($_GET["stinfo"])?$_GET["stinfo"]:exit(1);
echo $stinfo;
$pid = posix_getpid();
$fp = fopen(“usrinfo.txt”,”a+”);
fwrite($fp,”user:”.$usrinfo.” stinfo:”.$stinfo.”–”.$pid.”\n”);
fclose($fp);
运行ab命令:ab -n 1000 -c 100 “http://localhost:9080/test.php”(ab是apache自带的网络测试工具-n 1000表示共发起1000次请求,-c 100表示模拟每次100个请求),查看usrinfo.txt文件结果,文件输出 -
user:usr_test stinfo:st_test–22311–47947
user:usr_test stinfo:st_test–22346–2782
user:usr_test stinfo:st_test–22285–55237
user:usr_test stinfo:st_test–22305–63849
user:usr_test stinfo:st_test–22288–9748
user:usr_test stinfo:st_test–22296–37314
……………………………………………
…………………………………………….
user:usr_test stinfo:st_test–22311–37033
………………………………………………
——————————————-
user:usr_test stinfo:st_test–22346–16242
………………………………………………..
/* 以上结果共1000行 */
由于我们的ab测试发起1000次请求,所以最终结果是1000行,并且fastcgi维护的是一个装有php-cgi的进程池,使php-cgi解析了一次php程序作业以后不自动销毁而是再次解析其他的php程序作业。因此有如图所示的22311号进程处理完毕一个作业以后再次处理其他作业。
通过结果我们看到,我们程序输出的数据是正确的。而没有出现由于fastcgi并发执行而导致的写操作乱序现象。事实上,PHP的fwrite函数调用了C的fwrite函数(缓冲写),C语言的fwrite函数调用了Linux系统调用write,而无论fwrite或者write函数都是原子操作,是不可分割的,所以上述的代码段可以正常运行。
如果我们将一次fwrite调用修改成两次,代码如下:
$usrinfo = isset($_GET["usrinfo"])?$_GET["usrinfo"]:exit(1);
$stinfo = isset($_GET["stinfo"])?$_GET["stinfo"]:exit(1);
echo $stinfo;
$pid = posix_getpid();
$fp = fopen(“usrinfo.txt”,”a+”);
$num = rand(0,100000);
fwrite($fp,”user:”.$usrinfo.” stinfo:”.$stinfo.”–”.$pid.”–”.$num.”\n”);
fwrite($fp,”talking 1 — pid:$pid and num:$num\n”);
fclose($fp);
再次运行 ab -n 1000 -c 100 “http://localhost:9080/test.php”,则再次打开usrinfo.txt
user:usr_test stinfo:st_test–22319–96796
user:usr_test stinfo:st_test–22303–79356
talking 1 — pid:22319 and num:96796
talking 1 — pid:22303 and num:79356
user:usr_test stinfo:st_test–22293–23359
user:usr_test stinfo:st_test–22309–48593
talking 1 — pid:22293 and num:23359
talking 1 — pid:22309 and num:48593
user:usr_test stinfo:st_test–22338–22198
user:usr_test stinfo:st_test–22316–75875
talking 1 — pid:22338 and num:22198
talking 1 — pid:22316 and num:75875
……………………………………………………………………………………..
我们看到,程序的写顺序已经被完全破坏,如果我们把spawn-cgi开启php-cgi的数减少到1(./spawn-cgi -p 9000 -F 1 -f ~/php-cgi),则该程序结果运行正常,而如果这样,php-cgi就变成了单进程了,而-cgi开启的php-cgi越多,该程序的结果就会越混乱。
其实,很多时候,我们并没有考虑我们php代码的并行能力,尤其是在我们的php代码对某个资源可读可写的时候。但这并不是说php的所有操作就都是原子的,事务的,可并行的。由于我们的php脚本是运行在fastcgi容器中,而fastcgi是多进程的,所以如果php程序访问了临界资源,势必造成程序结果的不正确性。
解决问题的办法是使用锁机制。php没有继承posix标准支持的unix锁:比如记录锁fcntl,线程锁等,而只封装了一个linux系统调用flock(信号量也能做成锁,这个以后会给大家分享用法),flock形式为flock($fp,$type),其中$fp为文件句柄,而$type为
/* 当一个文件的打开方式是可读可写的,通常需要向文件加入锁机制 */
1. LOCK_SH 共享锁:
通常为进程向文件请求读操作时需加共享锁。共享锁可支持任意个进程间的读操作,如果写一个加了共享锁的文件则进程阻塞进入SLEEP状态值到共享锁解锁
2. LOCK_EX 独占锁:
通常为进程向文件的写操作加独占锁,一旦文件加上了该锁,则其他任意进程访问该文件时都会阻塞,直到解锁为止。
3. LOCK_UN 解锁
为加锁的文件句柄解锁
这样的加锁方式必然可以保证加锁程序块的原子性,但同时也牺牲了程序的效率,因此,我们实际的程序中应该在程序的加锁和解锁代码间嵌入尽量少的程序逻辑(尤其是独占锁),保证程序尽快解锁。
最后,附上加上锁机制以后的程序:
$usrinfo = isset($_GET["usrinfo"])?$_GET["usrinfo"]:exit(1);
$stinfo = isset($_GET["stinfo"])?$_GET["stinfo"]:exit(1);
echo $stinfo;
$pid = posix_getpid();
$fp = fopen(“usrinfo.txt”,”a+”);
$num = rand(0,100000);
flock($fp,LOCK_EX);
fwrite($fp,”user:”.$usrinfo.” stinfo:”.$stinfo.”–”.$pid.”–”.$num.”\n”);
fwrite($fp,”talking 1 — pid:$pid and num:$num\n”);
flock($fp,LOCK_UN);
fclose($fp);
运行该程序,产生正确的结果。
user:usr_test stinfo:st_test–22301–63987
talking 1 — pid:22301 and num:63987
user:usr_test stinfo:st_test–22323–81895
talking 1 — pid:22323 and num:81895
user:usr_test stinfo:st_test–22302–66557
talking 1 — pid:22302 and num:66557
user:usr_test stinfo:st_test–22282–82967
talking 1 — pid:22282 and num:82967
user:usr_test stinfo:st_test–22333–6534
talking 1 — pid:22333 and num:6534
PHP有没有 原子性,PHP程序的原子性和PHP的文件锁相关推荐
- java 并发的原子性_Java并发教程–原子性和竞争条件
java 并发的原子性 原子性是多线程程序中的关键概念之一. 我们说一组动作是原子的,如果它们都以不可分割的方式作为单个操作执行. 认为多线程程序中的一组操作将被串行执行是理所当然的,可能会导致错误的 ...
- redis setnx原子性_不支持原子性的 Redis 事务也叫事务吗?
文章收录在 GitHub JavaKeeper ,N线互联网开发必备技能兵器谱 假设现在有这样一个业务,用户获取的某些数据来自第三方接口信息,为避免频繁请求第三方接口,我们往往会加一层缓存,缓存肯定要 ...
- java原子性是什么,java 原子性 可见性 有序性
原子性 原子性是指一个操作或多个操作要么全部执行完成且执行过程不被中断,要么就不执行. 如向变量x赋值操作 x = 10 是原子性的,就不会出现赋值操作进行到一半(x的低16位赋值成功,高16位没有赋 ...
- Linux管道的原子性,管道的原子性 linux写操作原子性
从本质上说,管道也是一种文件,但他又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题 限制管道的大小.实际上,管道是一个固定大小的缓冲区.在Linux中该换冲区的大小为一页,4k 使得他的 ...
- mysql 事务原子性_数据库事务原子性、一致性是怎样实现的?
先借用前辈的一句话:数据库事务有不同的隔离级别,不同的隔离级别对锁的使用是不同的,锁的应用最终导致不同事务的隔离级别. 隔离性分为四个级别: 1读未提交:(Read Uncommitted) 2读已提 ...
- 多线程:Vector是线程安全的吗
线程安全,在java的多并发编程中是重要概念,意思是,多个线程同时操作一个对象,在各种不同情况下,都不会造成不同的后果. 一个经典问题,Vector到底是不是线程安全的? 很多人都会回答,是,vect ...
- java vector 线程安全_关于Vector到底是不是 线程安全的 问题
线程安全,在java的多并发编程中是重要概念,意思是,多个线程同时操作一个对象,在各种不同情况下,都不会造成不同的后果. 一个经典问题,Vector到底是不是线程安全的? 很多人都会回答,是,vect ...
- 操作系统的运行机制与体系
1.运行机制: (1)两种指令: 什么是指令? 指令就是处理器能识别和执行的最基本的命令. 一条指令通常由两个部分组成:操作码+地址码. 操作码:指明该指令要完成的操作的类型或性质,如取数.做加法或输 ...
- Vector的线程安全问题
首先提一个问题,Vector是线程安全的吗? 很多人都会回答:vector是线程安全的. 确实,通过查看JDK源码中,Vector确实是线程安全的,至少Vector的作者是这么说的. 通过观察源码,发 ...
最新文章
- Reporting Service 2012 体系结构
- matlab中udt函数,《MATLAB信号处理超级学习手册》——2.5 离散时间信号中的运算...
- JAVA入门级教学之(public class和class的区别)
- Tensorflow No module named ‘tensorflow.examples.tutorials‘解决办法,有用
- 时间加减计算器_小学生苦练加减乘除计算“基本功”,有没有必要?
- android dbinfo函数,android SQLiteDatabase中的update函数用法?
- VC++ 设置桌面壁纸
- vmware 详细安装教程
- openwrt编译smartdns_【萌新理解交流】浅谈openWRT中的smartDNS中各个选项如何设置及其含义。...
- 结构化程序设计知识点总结
- Unity中使用TimeSpane计算时差
- [渝粤教育] 山东职业学院 话说铁道 参考 资料
- 究竟什么是项目管理?它的主要内容是什么呢?
- php网站mercury安装,mercury300m无线路由器设置教程 教你正确安装无线路由器
- IDEA2020.1基于Maven开发spring cloud项目报错 程序包 com.xxx.xxx.xxx不存在
- JS 字符串中间加空格
- NiFi Processors之ReplaceText
- 超越Teamviewer,使用开源软件Rustdesk自建服务器实现远程桌面连接(10分钟包教会,超详细教程)
- Unity Animation Miss Path
- C4D多边形建模笔记