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);
        }
    }
}

Last modification:February 9th, 2020 at 05:55 pm
小编很用心去写文章的,大家伙们给点支持呗。