在開(kāi)發(fā)的過(guò)程中,我們可能會(huì)用到php exec函數(shù)來(lái)執(zhí)行一些外部命令。這個(gè)函數(shù)可以幫助我們快速地執(zhí)行系統(tǒng)命令,比如解壓文件、轉(zhuǎn)換圖片等等。
不過(guò),你可能會(huì)發(fā)現(xiàn),有時(shí)候使用exec函數(shù)會(huì)出現(xiàn)阻塞的情況:
$command = "sleep 10"; exec($command); echo "done";
在上面的例子中,我們使用了exec函數(shù)執(zhí)行了一個(gè)簡(jiǎn)單的命令,讓程序停頓10秒鐘。但是,在程序執(zhí)行完exec函數(shù)之后,居然沒(méi)有立即輸出done。相反,程序需要等待10秒鐘之后才會(huì)輸出done。這就出現(xiàn)了阻塞。
那么,什么是阻塞呢?簡(jiǎn)單來(lái)說(shuō),阻塞指的是一個(gè)進(jìn)程(或線程)在執(zhí)行某個(gè)操作時(shí),需要等待另一個(gè)進(jìn)程(或線程)的響應(yīng)才能繼續(xù)進(jìn)行。在上面的例子中,程序需要等待sleep命令執(zhí)行完畢才能輸出done,因此就出現(xiàn)了阻塞。
那么,如何解決這個(gè)問(wèn)題呢?其實(shí),我們可以使用多線程來(lái)解決這個(gè)問(wèn)題。在上面的例子中,我們可以使用PHP的多線程擴(kuò)展,將exec函數(shù)的執(zhí)行放到一個(gè)子線程里面,主線程可以繼續(xù)執(zhí)行。代碼如下:
$command = "sleep 10"; $pid = pcntl_fork(); if ($pid == -1) { echo "fork failed"; } else if ($pid == 0) { exec($command); exit(0); } else { echo "done"; }
在上面的代碼中,我們使用了pcntl_fork函數(shù)來(lái)創(chuàng)建一個(gè)子進(jìn)程,然后在子進(jìn)程中執(zhí)行exec函數(shù)。在主進(jìn)程中,我們可以繼續(xù)執(zhí)行其他操作。這樣,就可以避免阻塞的問(wèn)題。
需要注意的是,在使用多線程解決exec阻塞的問(wèn)題時(shí),需要特別小心。因?yàn)槎嗑€程的代碼比較復(fù)雜,而且容易出現(xiàn)各種問(wèn)題。如果沒(méi)有必要使用多線程,建議還是不要使用。
除了使用多線程之外,我們還可以使用stream_select函數(shù)來(lái)解決阻塞的問(wèn)題。在某些情況下,使用stream_select函數(shù)的效果可能會(huì)更好。具體的方法可以參考下面的代碼:
$command = "sleep 10"; $descriptors = array( 0 =>array("pipe", "r"), // stdin is a pipe that the child will read from 1 =>array("pipe", "w"), // stdout is a pipe that the child will write to 2 =>array("pipe", "w") // stderr is a pipe that the child will write to ); $process = proc_open($command, $descriptors, $pipes, NULL, NULL); if (is_resource($process)) { fclose($pipes[0]); stream_set_blocking($pipes[1], 0); stream_set_blocking($pipes[2], 0); $read = array($pipes[1], $pipes[2]); $write = NULL; $except = NULL; $result = stream_select($read, $write, $except, 10); if ($result !== false) { foreach ($read as $pipe) { echo stream_get_contents($pipe); } } fclose($pipes[1]); fclose($pipes[2]); $exit_code = proc_close($process); echo "done"; }
在上面的代碼中,我們首先使用proc_open函數(shù)來(lái)執(zhí)行命令。然后,關(guān)閉輸入管道,設(shè)置輸出管道為非阻塞模式,使用stream_select函數(shù)來(lái)等待輸出管道的數(shù)據(jù),最后關(guān)閉輸出管道和錯(cuò)誤管道,以及關(guān)閉進(jìn)程。這樣,就可以避免exec函數(shù)阻塞的問(wèn)題了。
總之,在使用php exec函數(shù)時(shí),需要特別小心阻塞的問(wèn)題。如果我們需要執(zhí)行一些比較耗時(shí)的命令,可以考慮使用多線程或stream_select函數(shù)來(lái)避免阻塞。