PHP是一種腳本語言,在設計上它是一種動態語言,在運行時才確定代碼內變量的類型和使用。大多數情況下,這都很適合像網站這樣的動態環境,并且對于開發者而言也很方便。然而,這樣的設計有時也會出現一些問題。在PHP使用pthread時,就面臨著一個很大的問題:內存。
當使用多線程時,我們必須考慮同時運行的所有線程仍然需要相同的代碼。這會導致所有線程都會在內存中同時擁有同一個代碼副本,因此影響相當大的資源消耗。舉個例子,如果我們想要同時對同一個大型數組進行操作,那么每個線程都必須在內存中加載該數組的完整副本。這可能導致內存急劇耗盡,使應用程序崩潰或變得緩慢。這時就需要使用一些技巧來減少內存的使用。
有一種方法可以減少內存消耗,即使用共享內存。在多線程環境中,可以使用共享變量來讓所有線程共享該變量的內存,這樣就不會重復加載同樣的內容,極大地減少了內存使用率。PHP的pthread繼承了這個思想,可以使用pthreads中的Shared Memory Objects擴展來實現共享內存。
class SharedArray extends \Collectable { private $id; private $shm; public function __construct() { $this->shm = shmop_open(ftok(__FILE__, chr(getmypid())), 'c', 0644, count($this)); $this->id = intval(shmop_read($this->shm, 0, 4)); shmop_write($this->shm, pack('L', $this->id + 1), 0); } public function run() { $this[] = self::worker(); } public function worker() { $id = ++$this->id; sleep(mt_rand(1, 10)); return $id; } public function offsetSet($offset, $value) { $value = is_int($value) ? pack('L', $value) : $value; return shmop_write($this->shm, $value, $this->getOffset($offset)); } public function offsetGet($offset) { return unpack('L', shmop_read($this->shm, $this->getOffset($offset), 4))[1]; } public function count() { return 4096; } private function getOffset($offset) { return $offset * 4 + 4; } } $array = new SharedArray(); $pool = new \Pool(16); for($i = 0; $i < 4096; $i++) { $pool->submit($array); } $pool->collect(); var_dump($array->toArray());
在上面的代碼中,我們定義了一個SharedArray類,它代表了我們想要在多個線程中共享的數組。在這個類中,我們使用了System V共享存儲器來實現共享內存。在構造函數中,我們使用shmop_open創建共享存儲器變量,并且使用ftok系統調用返回一個鍵用于標識變量。我們在構造函數中使用共享存儲器來存儲數組索引,所有需要共享的變量都會從這個SharedArray類中繼承。
我們還定義了offsetSet和offsetGet方法,這兩個方法在此繼承中被Pool Submit使用,可以使所有工作線程能夠直接訪問共享內存區,從而避免了重復內存拷貝。在運行時,我們創建了一個包含4096項作業的線程池,并通過Pool submit共享數組對象以創建工作線程。在工作線程完成操作后,它們返回的新數組值被立即保存在共享數組中。
通過這樣的方式,在 PHP 的多線程環境中使用共享內存,減少了系統對內存的消耗,進而提升了并行運行程序時的性能,適應了現如今對高性能代碼的更高要求。