Loading... # PHP 中多进程同时处理一个文件 (加锁中出现的问题) > 如果同时有两个进程同时写入一个文件(可能会导致文件插入乱序) > 那么当一个进程写入文件的时候,就要告诉另外一个进程,我在写入这个文件,你等我操作完再操作。 ## 1) pcntl_fork() 函数介绍 > `pcntl_fork()` 函数是 `php-pcntl` 模块中用于创建进程的函数。(不支持windows) * a) 当 `pcntl_fork()` 创建子进程成功后,子进程内 pid = 0,父进程则是等于子进程的 pid,失败则返回-1 * b) 子进程会复制父进程的代码、数据。那么就说明:子,父进程拥有的代码和数据会一模一样。 * c) 重点:子进程会复制父进程的状态,也就是说 `pcntl_fork()` 之前的声明和赋值的变量是在子进程中存在的。 ```php for ($i = 0; $i < 3; $i++) { $pid = pcntl_fork(); } sleep(30); ``` > pcntl_fork fork 后的子进程会在父进程执行的地方开始执行。 > 上面这个代码一个有 7 个子进程,1 个父进程 > 父进程创建了 3 个子进程, > 第一个子进程创建了 2 个子进程, > 第二个子进程创建 1 个进程, > 第一个子进程的子进程又创建 1 个子进程。 ![](media/15602456107621/16621042504358.jpg) ## 2) 子进程与父进程同一把锁 > 因为是先打开文件取得句柄,子进程和父进程都是使用同一个句柄。 > 子进程与父进程持有同一把锁 > 若子进程已经加锁,父进程又加锁,不会报错,只是更新加锁的状态。 ```php <?php $fp = fopen("demo.log", 'ab'); $pid = pcntl_fork(); if ($pid === 0) { // pid === 0 , in child process if (flock($fp, LOCK_EX)) { echo 'child process lock successfully', PHP_EOL; sleep(10); //@todo child process handling logic } } elseif ($pid > 0) { // pid > 0 , in main process if (flock($fp, LOCK_EX)) { echo 'main process lock successfully', PHP_EOL; //@todo main process handing logic } } else { // pid < 0 , create child process failed exit(-1); } ``` ## 3) 不是同一个文件句柄,同时给一个文件加锁会产生阻塞 > fopen 打开两次同一文件 或者 先 pcntl_fork() 子程序,然后再 fopen 文件 > 会以不同方式产生句柄,所以是两个不同的锁,如果同时加锁,会产生阻塞 ```php function blockByItself() { $fd1 = fopen('lock.txt', 'w+'); $fd2 = fopen('lock.txt', 'w+'); flock($fd1, LOCK_EX); flock($fd2, LOCK_EX); // wait } ``` ```php <?php $pid = pcntl_fork(); $fp = fopen('..' . "/tmp/lock.log", 'ab'); if ($pid === 0) { // sleep(1); if (flock($fp, LOCK_EX) === true) { // wait echo sprintf('[%s] Child process get lock successfully %s', date('Y-m-d H:i:s'), PHP_EOL); flock($fp, LOCK_UN); } else { echo sprintf('[%s] Child process cannot get lock %s', date('Y-m-d H:i:s'), PHP_EOL); } } elseif ($pid > 0) { // sleep(1); if (flock($fp, LOCK_EX) === true) { echo sprintf('[%s] Main process get lock successfully %s', date('Y-m-d H:i:s'), PHP_EOL); sleep(5); flock($fp, LOCK_UN); } else { echo sprintf('[%s] Main process get lock failed , %s', date('Y-m-d H:i:s'), PHP_EOL); } sleep(10); } else { exit(-1); } ``` ## 4) pcntl_fork() 多进程正确处理 ```php <?php $pid = pcntl_fork(); $fp = fopen('..' . "/tmp/output.log", 'ab'); if ($pid === 0) { for ($i = 1; $i < 10; $i++) { if (flock($fp, LOCK_EX) === true) { echo sprintf('[%s] Child process get lock successfully %s', date('Y-m-d H:i:s'), PHP_EOL); fwrite($fp, sprintf('[%s] ', date('Y-m-d H:i:s'))); fflush($fp); fwrite($fp, '十年生死两茫茫,'); fflush($fp); fwrite($fp, '不思量,'); fflush($fp); fwrite($fp, '自难忘。'); fflush($fp); fwrite($fp, '千里孤坟,'); fflush($fp); fwrite($fp, '无处话凄凉。' . PHP_EOL); fflush($fp); flock($fp, LOCK_UN); usleep(500000); } else { echo sprintf('[%s] Child process cannot get lock %s', date('Y-m-d H:i:s'), PHP_EOL); } } } elseif ($pid > 0) { for ($i = 1; $i < 10; $i++) { if (flock($fp, LOCK_EX) === true) { echo sprintf('[%s] Main process get lock successfully %s', date('Y-m-d H:i:s'), PHP_EOL); fwrite($fp, sprintf('[%s] ', date('Y-m-d H:i:s'))); fflush($fp); fwrite($fp, '唧唧复唧唧,'); fflush($fp); fwrite($fp, '木兰当户织,'); fflush($fp); fwrite($fp, '不闻机杼声,'); fflush($fp); fwrite($fp, '惟闻女叹息。' . PHP_EOL); fflush($fp); flock($fp, LOCK_UN); usleep(500000); } else { echo sprintf('[%s] Main process get lock failed , %s', date('Y-m-d H:i:s'), PHP_EOL); } } sleep(20); } else { exit(-1); } ``` © Allow specification reprint Support Appreciate the author AliPayWeChat Like If you think my article is useful to you, please feel free to appreciate