在所有PHP进程之间共享变量/内存

时间:2011-04-09 14:37:23

标签: php shared-memory

是否可以在所有PHP进程之间共享变量和数组而不重复它们

使用memcached,我认为PHP重复使用的内存:
$array = $memcache->get('array');
$ array将包含memcached的副本。

所以我的想法是,可能有一个已经定义的静态变量,并在所有进程之间共享。

5 个答案:

答案 0 :(得分:19)

使用Shmop

  

Shmop是一组易于使用的函数,允许PHP读取,   编写,创建和删除Unix共享内存段。

来自:http://www.php.net/manual/en/intro.shmop.php

构建此扩展程序无需外部库。

共享内存函数

  • shmop_close - 关闭
  • 共享内存块
  • shmop_delete - 删除共享内存块
  • shmop_open - 创建或打开共享内存块
  • shmop_read - 从共享内存块中读取数据
  • shmop_size - 获取共享内存块的大小
  • shmop_write - 将数据写入共享内存块

基本用法

// Create 100 byte shared memory block with system id of 0xff3
$shm_id = shmop_open(0xff3, "c", 0644, 100);
if (!$shm_id) {
    echo "Couldn't create shared memory segment\n";
}

// Get shared memory block's size
$shm_size = shmop_size($shm_id);
echo "SHM Block Size: " . $shm_size . " has been created.\n";

// Lets write a test string into shared memory
$shm_bytes_written = shmop_write($shm_id, "my shared memory block", 0);
if ($shm_bytes_written != strlen("my shared memory block")) {
    echo "Couldn't write the entire length of data\n";
}

// Now lets read the string back
$my_string = shmop_read($shm_id, 0, $shm_size);
if (!$my_string) {
    echo "Couldn't read from shared memory block\n";
}
echo "The data inside shared memory was: " . $my_string . "\n";

//Now lets delete the block and close the shared memory segment
if (!shmop_delete($shm_id)) {
    echo "Couldn't mark shared memory block for deletion.";
}
shmop_close($shm_id);

答案 1 :(得分:8)

在PHP进程之间共享内存的一种方法是安装像APC这样的PHP字节码缓存。 APC主要用于将字节码存储到OS管理的共享内存段中,但它还具有用于在进程之间共享任何内容的API(如本地版本的memcache)。

<?php
   $foobar = array('foo', 'bar');
   apc_store('foobar', $foobar);
?>

然后在其他地方:

<?php
    $foobar = apc_fetch('foobar');
    var_dump($foobar);
?>

共享内存的一个大问题是,两个进程很容易互相踩踏。因此共享内存最适合那些不会发生太大变化的事情,比如大型全局数组。

答案 2 :(得分:4)

PHP有魔术方法:

  • __get($property)让我们在对象上实现$ property的访问权限
  • __set($property, $value)让我们在对象上实现$ property的赋值

PHP可以序列化变量:

  • serialize($variable)返回变量
  • 的字符串表示形式
  • unserialize($string)返回字符串
  • 中的变量

PHP可以使用并发访问管理来处理文件:

  • fopen($file, 'c+')打开一个启用了建议锁定选项的文件(允许您使用flock)
  • flock($descriptor, LOCK_SH)使用共享锁(供阅读)
  • flock($descriptor, LOCK_EX)采用独占锁定(用于写入)

因此,在应用程序之间共享对象的最简单方法是创建一个类,该类实现并使用所有这些东西来保存并立即将其所有数据恢复到文件中。

该类的简单实现可以是:

class Synchro
{

   private $_file;

   public function __construct($file)
   {
       $this->_file = $file;
   }

   public function __get($property)
   {
       // File does not exist
       if (!is_file($this->_file))
       {
           return null;
       }

       // Check if file is readable
       if ((is_file($this->_file)) && (!is_readable($this->_file)))
       {
           throw new Exception(sprintf("File '%s' is not readable.", $this->_file));
       }

       // Open file with advisory lock option enabled for reading and writting
       if (($fd = fopen($this->_file, 'c+')) === false)
       {
           throw new Exception(sprintf("Can't open '%s' file.", $this->_file));
       }

       // Request a lock for reading (hangs until lock is granted successfully)
       if (flock($fd, LOCK_SH) === false)
       {
           throw new Exception(sprintf("Can't lock '%s' file for reading.", $this->_file));
       }

       // A hand-made file_get_contents
       $contents = '';
       while (($read = fread($fd, 32 * 1024)) !== '')
       {
           $contents .= $read;
       }

       // Release shared lock and close file
       flock($fd, LOCK_UN);
       fclose($fd);

       // Restore shared data object and return requested property
       $object = json_decode($contents);
       if (property_exists($object, $property))
       {
           return $object->{$property};
       }

       return null;
   }

   public function __set($property, $value)
   {
       // Check if directory is writable if file does not exist
       if ((!is_file($this->_file)) && (!is_writable(dirname($this->_file))))
       {
           throw new Exception(sprintf("Directory '%s' does not exist or is not writable.", dirname($this->_file)));
       }

       // Check if file is writable if it exists
       if ((is_file($this->_file)) && (!is_writable($this->_file)))
       {
           throw new Exception(sprintf("File '%s' is not writable.", $this->_file));
       }

       // Open file with advisory lock option enabled for reading and writting
       if (($fd = fopen($this->_file, 'c+')) === false)
       {
           throw new Exception(sprintf("Can't open '%s' file.", $this->_file));
       }

       // Request a lock for writting (hangs until lock is granted successfully)
       if (flock($fd, LOCK_EX) === false)
       {
           throw new Exception(sprintf("Can't lock '%s' file for writing.", $this->_file));
       }

       // A hand-made file_get_contents
       $contents = '';
       while (($read = fread($fd, 32 * 1024)) !== '')
       {
           $contents .= $read;
       }

       // Restore shared data object and set value for desired property
       if (empty($contents))
       {
           $object = new stdClass();
       }
       else
       {
           $object = json_decode($contents);
       }
       $object->{$property} = $value;

       // Go back at the beginning of file
       rewind($fd);

       // Truncate file
       ftruncate($fd, strlen($contents));

       // Save shared data object to the file
       fwrite($fd, json_encode($object));

       // Release exclusive lock and close file
       flock($fd, LOCK_UN);
       fclose($fd);

       return $value;
   }

}

现在,您可以像stdClass一样使用此类,但在构建时使用文件路径。

$obj = new Synchro("/tmp/test.sync"); 
$obj->hello = 'world';

// ... and in another process...
echo $obj->hello;

这个例子当然非常简单,它关注对文件的并发访问,但不关注变量,在更好的实现中,你将使用类似互斥锁的锁。

我刚刚在github上推了这个类(在完成它之后),你可以找到它here

答案 3 :(得分:3)

默认情况下,它根本不可能。每个解决方案都会将内容复制到当前范围内,因为如果没有,则无法访问它。

我不知道,究竟想要做什么,但也许你可以在“外部”执行此操作,例如作为gearman作业,然后只捕获进程的结果,而不是整个数组。

您还可以考虑将“大”数组拆分为切片,然后始终从apc或memcached中检索当前需要的部分。

答案 4 :(得分:0)

编辑:
你可能错误地使用共享内存。
你的共享内存本身就是这样的数组。因此,您必须将单独的多语言字符串直接存储在共享内存中,而不是将它们存储在大数组中。
然后只拉动特定页面上需要的字符串 就这样。

通常,要处理某些数据,程序必须通过将其存储在变量中来“复制”它 这就是变量的用途 - 存储(或“复制”)一些外部数据 例如,如果您的数据库中有一些用户信息,要在网页上显示用户名,您必须“复制”此数据,首先将其存储在PHP变量中。
等等。

你是第一个认为需要改变这种方法的人。