最近做电影票兑换项目遇到了大家同时兑换同一张电影票的情况,就使用了文件锁的方式来解决少量的并发!php多进程兑换电影票,通过读写同一个文件锁来解决并发!下面一起来看看!

<?php$fp = fopen("ticket/ticket.txt", "a+");
//第一个人请求的时候,第二个人等待访问
if (flock($fp, LOCK_EX)) {  // 进行排它型锁定//此处可以添加事物//开启事物$model = new \Think\Model(); $model->startTrans();//兑换流程(数据库操作,添删改查这里写)if(错误){$model->rollback();}//兑换成功时,提交事物$model->commit();flock($fp, LOCK_UN);    // 释放锁定
} else {echo "文件正在被其他程序占用";
}fclose($fp);

具体了解下PHP中的文件锁

可能存在多个进程读写同一个文件的问题,比如多进程程序读写同一个日志文件,这样就有必要解决读写同一个文件时加锁的问题,php已经内置了一个读写的文件锁方法flock,,官方的解释是轻便的文件咨询锁定,这很官方。

flock的第一个参数是一个文件句柄,第二个参数可以设置锁定方式,有几个常量可以设置,下面一一介绍。

LOCK_SH : 取得共享锁定(读取的程序)也就是常说的共享锁,该进程只能读不能写,其他进程还是能读取该文件的。

LOCK_EX:取得独占锁定(写入的程序)常说的独占锁,该进程能读写该文件,其他进程则不能读写。

LOCK_UN:释放锁定(无论共享或独占)也就是释放上述两种锁。

LOCK_NB:配合LOCK_SH和LOCK_EX使用,使得在加锁时程序非阻塞。

再举几个简单的示例说明下:

-------------------------------------------------------------------------------------------------------------------------------

一、协同锁(advisory lock) 和 强制锁 (mandatory lock)

1、协同锁
  协同锁要求参与操作的进程之间协同合作。假设进程“A”获得一个WRITE锁,并开始向文件中写入内容;此时,进程“B”并没有试图获取一个锁,它仍然可以打开文件并向文件中写入内容。在此过程中,进程“B”就是一个非合作进程。如果进程“B”试图获取一个锁,那么整个过程就是一个合作的过程,从而可以保证操作的“序列化”。
  只有当参与操作的进程是协同合作的时候,协同锁才能发挥作用。协同锁有时也被称为“非强制”锁。

2、强制锁
  强制锁不需要参与操作的进程之间保持协同合作。它利用内核来查检每个打开、读取、写入操作,从而保证在调用这些操作时不违反文件上的锁规则。

而flock使用协同锁,它要求进程都要遵守先拿锁,后操作的约定,这样才能实现文件锁的功能。

二、在介绍后续的内容之前,首先我们还要了解一下linux内核对于打开文件的处理机制,以下摘自《linux/unix系统编程手册》一书第5.4节

从上面的介绍可以知道,复制文件描述符(通过fork创建子进程或者dup系统调用)之后这些文件描述符指向内核中的同一个打开文件句柄,而进程每次调用fopen打开一个文件都会在内核中维护一个新的打开文件句柄

例如:

<?php//例程$fp = fopen("demo.log", "a");$pid = pcntl_fork();if ($pid == 0){echo "子进程\n";} else {echo "父进程\n";}

上面例程一中先打开一个文件,然后fork,相当于是复制了文件描述符,父子进程中的文件句柄指向内核中同一个打开文件句柄。

<?php//例程二$pid = pcntl_fork();$fp = fopen("demo.log", "a");if ($pid == 0){fwrite($fp, "子进程\n");} else {fwrite($fp, "父进程\n");}

而这个例程二是先fork,然后父子进程分别调用了一次fopen,这时父子进程的文件句柄指向内核中的不同的打开文件句柄,虽然它们打开的是同一个文件。

三、flock锁是基于内核中打开文件句柄的
前面之所以大费周章的介绍内核打开文件的数据结构,正是由于flock施加的锁是基于内核中打开的文件句柄,也就是说指向内核中同一个打开文件句柄的文件描述符(或文件句柄)是共享一个文件锁的,对其中任何一个文件句柄的加锁操作都会反映到其他的文件句柄。对于一个已经获得锁的内核打开文件句柄,再次加锁会先释放之前的锁,然后再次加新锁,可以理解是更新了 一次锁

<?php$fp = fopen("demo.log", "a");if(flock($fp, LOCK_EX)){echo "加锁成功\n";}if(flock($fp, LOCK_EX)){echo "加锁成功\n";}
上面这个例程虽然第一次加锁之后没有释放锁,但第二次加锁还是会成功,这就是更新锁的情况。
<?php$fp1 = fopen("demo.log", "a");$fp2 = fopen("demo.log", "a");if(flock($fp1, LOCK_EX)){echo "fp1加锁成功\n";}if(flock($fp2, LOCK_EX)){echo "fp2加锁成功\n";}

这个例程打开同一个文件两次,fp1和fp2指向不同的内核打开文件句柄,fp1获得锁后没有释放,结果fp2将获取不到锁而一直阻塞。

<?php//例程四$fp = fopen("demo.log", "a");$pid = pcntl_fork();if ($pid == 0){if(flock($fp, LOCK_EX)){echo "子进程加锁成功\n";while(1){sleep(1);}}} elseif($pid > 0) {sleep(1);if(flock($fp, LOCK_EX)){echo "父进程加锁成功\n";}}

上述例程四输出:

  1. 子进程加锁成功

  2. 父进程加锁成功

由于先打开文件然后fork,父子进程的文件句柄指向同一个内核打开文件句柄,父子进程每次加锁都相当于在更新同一个锁,所以虽然子进程先拿到了锁并且没有释放锁,父进程却仍然可以拿到锁,这本质上还是一种更新锁的情况,flock并没有达到并发控制的目的。

<?php//例程五$pid = pcntl_fork();$fp = fopen("demo.log", "a");if ($pid == 0){if(flock($fp, LOCK_EX)){echo "子进程加锁成功\n";while(1){sleep(1);}}} elseif($pid > 0) {sleep(1);if(flock($fp, LOCK_EX)){echo "父进程加锁成功\n";}}

上述例程五中,先fork一个子进程,然后父子进程都用fopen打开文件,它们的文件句柄指向不同的内核打开文件句柄,所以当子进程拿到锁后,只要不释放锁,那么父进程将永远拿不到锁,这才是flock正确的使用场景。

四、flock的使用场景和示例代码

<?php$pid = pcntl_fork();$fp = fopen("log.txt", "a");if ($pid == 0){for($i = 0; $i < 1000; $i++){fwrite($fp, "黄河远上白云间,");fflush($fp);fwrite($fp, "一片孤城万仞山。");fflush($fp);fwrite($fp, "羌笛何须怨杨柳,");fflush($fp);fwrite($fp, "春风不度玉门关。\n");fflush($fp);}}else if ($pid > 0){for($i = 0; $i < 1000; $i++){fwrite($fp, "葡萄美酒夜光杯,");fflush($fp);fwrite($fp, "欲饮琵琶马上催。");fflush($fp);fwrite($fp, "醉卧沙场君莫笑,");fflush($fp);fwrite($fp, "古来征战几人回。\n");fflush($fp);}}

上面这个例子中创建一个子进程,然后父子进程以追加的模式分别打开同一个文件,父子进程向日志文件中分别循环写一首诗( 这里使用fflush每写一句就刷新文件缓冲,避免缓冲影响问题的显现),结束之后查看日志文件:

  1. 葡萄美酒夜光杯,欲饮琵琶马上催。醉卧沙场君莫笑,古来征战几人回。

  2. 葡萄美酒夜光杯,欲饮琵琶马上催。醉卧沙场君莫笑,古来征战几人回。

  3. 葡萄美酒夜光杯,欲饮琵琶马上催。黄河远上白云间,一片孤城万仞山。羌笛何须怨杨柳,春风不度玉门关。

  4. 黄河远上白云间,一片孤城万仞山。羌笛何须怨杨柳,春风不度玉门关。

  5. 黄河远上白云间,一片孤城万仞山。羌笛何须怨杨柳,春风不度玉门关。

可以看到上述代码的问题是一首诗还没写完,另一首诗就开始写了,结果破坏了诗的完整性,如果不想两首诗混在一起,那么就可以使用flock在开始写入一首诗之前加锁,写完之后释放锁。

真正的栗子来了

$pid = pcntl_fork();$fp = fopen("log.txt", "a");if ($pid == 0){for($i = 0; $i < 1000; $i++){if (flock($fp, LOCK_EX)){fwrite($fp, "黄河远上白云间,");fflush($fp);fwrite($fp, "一片孤城万仞山。");fflush($fp);fwrite($fp, "羌笛何须怨杨柳,");fflush($fp);fwrite($fp, "春风不度玉门关。\n");fflush($fp);flock($fp, LOCK_UN);}}}else if ($pid > 0){for($i = 0; $i < 1000; $i++){if (flock($fp, LOCK_EX)){fwrite($fp, "葡萄美酒夜光杯,");fflush($fp);fwrite($fp, "欲饮琵琶马上催。");fflush($fp);fwrite($fp, "醉卧沙场君莫笑,");fflush($fp);fwrite($fp, "古来征战几人回。\n");fflush($fp);flock($fp, LOCK_UN);}}}

希望以上的文件锁介绍对大家有用!

php多进程兑换电影票,通过读写同一个文件锁来解决并发!相关推荐

  1. 解决多进程或多线程同时读写同一个文件的问题

    解决多进程或多线程同时读写同一个文件的问题 PHP是没有多线程概念的,尽管如此我们仍然可以用"不完美"的方法来模拟多线程.简单的说,就是队列处理. 通过对文件进行 加锁和解锁 来实 ...

  2. pytorch 多进程读写同一个文件

    torch 读写同一个图片 # -*- coding:utf-8 -*- from threading import Threadimport bind_cv as demo import timei ...

  3. java多线程读取文件_java多线程读写同一个文件

    本文提供java多线程分别定时读写同一个文件的样例,其中两个线程,一个每分钟写入当前时间到指定文件,另一个线程读出每分钟新写的内容. 使用简单的Thread.sleep技术实现定时 package t ...

  4. mysql myisam 并发_MySQL的myisam解决并发读写解决方法

    MySQL的myisam解决并发读写解决方法MyISAM在读操作占主导的情况下是很高效的.可一旦出现大量的读写并发,同InnoDB相比,MyISAM的效率就会直线下降,而且,MyISAM和InnoDB ...

  5. python读取文件路径乱码 linux_Python之pandas读写文件乱码的解决方法

    Python之pandas读写文件乱码的解决方法 python读写文件有时候会出现 'XXX'编码不能打开XXX什么的,用记事本打开要读取的文件,另存为UTF-8编码,然后再用py去读应该可以了.如果 ...

  6. Python -- xlrd,xlwt,xlutils 读写同一个Excel

    最近开始学习python,想做做简单的自动化测试,需要读写excel,然后就找到了xlrd来读取Excel文件,使用xlwt来生成Excel文件(可以控制Excel中单元格的格式),需要注意的是,用x ...

  7. iframe 中 js 的 cookie 读写不到的解决办法

    先看一个例子比如:我们在 www.cr173.com 中用 iframe 了一个 www.fxxz.com 的一个页面. 如: < iframe height="100" m ...

  8. java 写文件 并发_记录一次Java文件锁引起的并发写文件问题

    背景 刚接手新项目,该项目是高并发的游戏日志服务端存储,一个项目适配多个游戏,很多特殊需求要兼容,刚开始接手,需要修复很多管道的数据,存储管道有两个,分别是MySQL和HDFS,数据消费自Kafka, ...

  9. mysql主从配置对解决并发有用_MySQL主从配置,读写分离

    大型网站为了缓解大量的并发访问,要网站实现分布式负载均衡.但是数据访问层,如果还是传统的数据结构,或者只是单单靠一台服务器扛,如此多的数据库连接操作,数据库必然会崩溃,数据丢失的话,后果不堪设想.这时 ...

  10. 记一次ARM-鲲鹏服务器读写parquet报错解决过程

    背景: 最近客户现场使用华为提供的ARM-鲲鹏服务器集群,使用spark2.4.0,输出数据格式为parquet时,下游流程再使用该输出作为输入时出现报错,报错日志如下: Caused by: jav ...

最新文章

  1. alt+shift+j,添加日期、作者等
  2. js 中的new Image
  3. 数据处理的两个基本问题05 - 零基础入门学习汇编语言42
  4. java品酒会,我学 rxjava 2(3)- 热发射
  5. AngularJS 1.3 支持使用 $digest() 循环实现延迟
  6. 互联网大佬学历背景大揭秘,看看是你的老乡还是校友
  7. 初学opengl的一些知识整理-1
  8. Oracle队列锁enq:TS,Temporary Segment (also TableSpace)
  9. mtk刷机报错4032专业维修教程(图文)
  10. 关于VB中Print函数在数组中换行的理解
  11. video call BT audio音频流程
  12. 十年职场软件工程师感悟
  13. 在AWS上的架构部署与设计
  14. STM32F103C6T6 | 模拟IIC主机读取AHT20温湿度传感器数据
  15. 纯CSS3流光边框特效
  16. cisco路由器启动过程
  17. Android布局文件中的xmlns:tools作用以及用法
  18. 跳过校园网开机直连宽带
  19. 新!《一天吸引大量精准流量》主动加你微信的方法,无需软件,告别大量推广,让对你产品感兴趣的客源主动加你!!!
  20. python-优矿-牛市价差和熊市价差组合策略

热门文章

  1. 百度给创新员工发2000w奖金........
  2. 编程高手与IT民工的区别在哪?
  3. insert用法小结
  4. chmod 777 到底是啥 ???看完这个你就完全懂了!
  5. 低代码技巧:甘特图制作步骤
  6. H3BPM实例分享——金额规则大写
  7. leetcode_1370. 上升下降字符串
  8. CentOS6.5_x64安装VNCserver
  9. 许丹萍 计算机系,【晋江市“十佳少先队辅导员”】第二实验小学老师许丹萍: 关注每一个队员的成长...
  10. 职能部门绩效考核指标设置