PHP 中多进程同时处理一个文件(加锁中出现的问题)
如果同时有两个进程同时写入一个文件(可能会导致文件插入乱序)
那么当一个进程写入文件的时候,就要告诉另外一个进程,我在写入这个文件,你等我操作完再操作。
pcntl_fork() 函数说明
pcntl_fork()
函数是php-pcntl
模块中用于创建进程的函数。(不支持windows)
- 当
pcntl_fork()
创建子进程成功后,在父进程内,返回0,在子进程内返回自身的进程号,失败则返回-1 - 子进程会复制父进程的代码、数据。那么就说明:子,父进程拥有的代码和数据会一模一样。
- 重点:子进程会复制父进程的状态,也就是说
pcntl_fork()
之前的声明和赋值的变量是在子进程中存在的。
for ($i = 0; $i < 3; $i++) {
$pid = pcntl_fork();
}
sleep(30);
上面这个代码一个有 7 个子进程,父进程创建了 3 个子进程,第一个子进程创建了 2 个子进程,第二个子进程创建 1 个进程,第一个子进程的子进程又创建 1 个子进程。
子进程与父进程同一把锁
因为是先打开文件取得句柄,子进程和父进程都是使用同一个句柄,
子进程与父进程持有同一把锁
若子进程已经加锁,父进程又加锁,不会报错,只是更新加锁的状态
$fd = fopen('lock.txt', 'w+');
flock($fd, LOCK_EX);
if (pcntl_fork() == 0) { // 子进程与父进程持有同一把锁
flock($fd, LOCK_UN); // 同时释放了父进程持有的锁
}
// $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";
// }
// }
若不是同用一个文件句柄,同时给一个文件加锁会产生阻塞
fopen 打开两次同一文件或者先 pcntl_fork() 子程序,然后在 fopen 文件
会以不同方式产生句柄,所以是两个不同的锁,如果同时加锁,会产生阻塞
function block_by_itself()
{
$fd1 = fopen('lock.txt', 'w+');
$fd2 = fopen('lock.txt', 'w+');
flock($fd1, LOCK_EX);
flock($fd2, LOCK_EX); // 阻塞
/**
*
* $pid = pcntl_fork();
* $fp = fopen('lock.txt', 'w+');
* flock($fd1, LOCK_EX);
* flock($fd2, LOCK_EX);
*/
}
多进程不用 flock() 同时向一个文件写入
<?php
$pid = pcntl_fork();
$fp = fopen("./no_flock.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);
}
}
多进程使用 flock() 同时向一个文件写入
<?php
$pid = pcntl_fork();
$fp = fopen("./flock.txt", "a");
if ($pid == 0) {
for ($i = 0; $i < 20; $i++) {
if (flock($fp, LOCK_EX)) {
fwrite($fp, "黄河远上白云间,");
fflush($fp);
fwrite($fp, "一片孤城万仞山。");
fflush($fp);
fwrite($fp, "羌笛何须怨杨柳,");
fflush($fp);
fwrite($fp, "春风不度玉门关。\n");
flock($fp, LOCK_UN);
}
}
} elseif ($pid > 0) {
for ($i = 0; $i < 20; $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);
}
}
}