patternphpMinor
Forking with PHP (4 different approaches)
Viewed 0 times
withphpforkingdifferentapproaches
Problem
I thought about three (now four) different ways how to execute forks with PHP.
What is the faster, maybe better solution and why?
-
PHP script included
-
PHP script executed through exec()
-
PHP script executed as background command
(additional) 4. Using Gearman
worker.php can open database connections, files, etc ...
Any good explanation would be much appreciated.
What is the faster, maybe better solution and why?
-
PHP script included
foreach($tasks as $task) {
$pid = pcntl_fork();
if($pid == -1) {
exit("Error forking...\n");
} else if($pid == 0) {
include 'worker.php';
exit();
}
}
while(pcntl_waitpid(0, $status) != -1);`-
PHP script executed through exec()
foreach($tasks as $task) {
$pid = pcntl_fork();
if($pid == -1) {
exit("Error forking...\n");
} else if($pid == 0) {
exec('php worker.php '.$task);
exit();
}
}
while(pcntl_waitpid(0, $status) != -1);-
PHP script executed as background command
foreach($tasks as $task) {
$workers[] = exec('php worker.php '.$task.' & echo $!');
}
do {
foreach($workers as $idx => $pid) {
if(!posix_getpgid($pid)) {
unset($workers[$idx]);
}
}
} while(!empty($workers));(additional) 4. Using Gearman
worker.php can open database connections, files, etc ...
Any good explanation would be much appreciated.
Solution
- All code embedded in a single PHP script. With explanation as requested.
The statements before the fork is executed normally.
After a succesful pcntl_fork(), the running process is cloned, complete with its current state (all variables, including process counter). So two identical processes continue from that point in code.
Almost identical: the only difference is the return values of pcntl_fork(), stored in both $pid's.
The cloned process is the parent, and receives the process_id of its clone.
The clone process is the child, and receives 0.
Depending on this value, both processes go their separate ways, and continue until the hit an exit().
If parent- and child-code are not individually terminated by exit(), then both may continue executing the common code at the end, resulting both processes printing "I am both"!.
Try for yourself, and inspect the output. Then uncomment the exit()'s and run it again.
$mypid = getmypid();
echo "$mypid: I am the parent. I have no child yet.\n";
// fork: a twin process is created
if(($pid = pcntl_fork()) == -1) { exit("Error forking...\n"); }
if($pid == 0) {
// Child code goes here
$mypid = getmypid();
echo "$mypid: I am the child. This is what I do.\n";
// exit();
}else{
// Parent code goes here
echo "$mypid: I am still the parent. I have a child now: it's number is $pid.\n";
// exit();
}
// Both parent and child execute this code unless
// the exit's above are uncommented.
echo "$mypid: we are both. The pid I know is: $pid\n";
exit();Output: of the script above. The $mypid at the beginning of the line is the process id of the printing process. The $pid on the right is the return-value of the fcntl_fork().
6346: I am the parent. I have no child yet.
6347: I am the child. This is what I do.
6346: I am still the parent. I have a child now: it's number is 6347.
6346: we are both. The pid I know is: 6347
6347: we are both. The pid I know is: 0Note: after the fcntl_fork() both processes work independently. So one may go faster than the other; they may run on different processes, etc.
The order of the output may differ on your computer !
You can have them synchronized by making the parent wait for the childs exit.
This was my attempt to give an explanation. Now the answer to your question, wich one is faster or better:
Essentially they are all the same, as far as the fork goes. The differences are only in th extras.
An include makes the 'compiler' translate the code all at once. And only once.
All variables set in the main program (before the fork ! ) are 'inherited' by the child.
An exec cleans all process variables and starts a new program, just as you do from the command line. So this is overhead. And no information is shared from the parent to the child. (unless you ... well, there are possibilities).
A new process in a clean memory also forces the child code to be compiled in the second process. So the compiler has to be started again; this to is overhead.
So without exec is always better ? No. For example: if your co-worker changes his child-code, his typo's will make the compiler stop compiling your code if is is included.
But executing the other code will clearly show where the typo's are made. Programs are insulated much better this way. At some cost. Depends on how often you want the fork to be executed.
Code Snippets
$mypid = getmypid();
echo "$mypid: I am the parent. I have no child yet.\n";
// fork: a twin process is created
if(($pid = pcntl_fork()) == -1) { exit("Error forking...\n"); }
if($pid == 0) {
// Child code goes here
$mypid = getmypid();
echo "$mypid: I am the child. This is what I do.\n";
// exit();
}else{
// Parent code goes here
echo "$mypid: I am still the parent. I have a child now: it's number is $pid.\n";
// exit();
}
// Both parent and child execute this code unless
// the exit's above are uncommented.
echo "$mypid: we are both. The pid I know is: $pid\n";
exit();6346: I am the parent. I have no child yet.
6347: I am the child. This is what I do.
6346: I am still the parent. I have a child now: it's number is 6347.
6346: we are both. The pid I know is: 6347
6347: we are both. The pid I know is: 0Context
StackExchange Code Review Q#22919, answer score: 6
Revisions (0)
No revisions yet.