[编辑] 完全重写并添加背景(下面的原始问题)
在 PHP 中运行的应用程序中,出于性能原因(数据库开销太大且文件太慢),我使用共享内存临时存储值。
我构建了一个非常简单的共享内存类,它允许脚本访问存储在共享内存中的变量,并且能够使用信号量同步调用。代码在这里(目前还没有错误处理):
class SHM {
private static $defaultSize = 10000;
private static function getIdentifier ($identFile, $projId) {
return ftok($identFile, $projId);
}
private $sem = NULL;
private $shm = NULL;
private $identFile;
private $projId;
private $size;
public function __construct($identFile, $projId, $size=NULL) {
if ($size === NULL) $size = self::$defaultSize;
$this->identFile = $identFile;
$this->projId = $projId;
$this->size = $size;
}
public function __destruct() {
if ($this->sem) {
$this->lock();
if ($this->shm) {
shm_detach($this->shm);
}
$this->free();
}
}
public function exists ($key) {
return shm_has_var($this->getShm(), $key);
}
public function get ($key, $lock=true) {
if ($this->exists ($key)) {
if ($lock) $this->lock();
$var = shm_get_var($this->getShm(), $key);
if ($lock) $this->free();
return $var;
} else return NULL;
}
public function set ($key, $var, $lock=true) {
if ($lock) $this->lock();
shm_put_var($this->getShm(), $key, $var);
if ($lock) $this->free();
}
public function remove ($key, $lock=true) {
if ($this->exists ($key)) {
if ($lock) $this->lock();
$result = shm_remove_var($this->getShm(), $key);
if ($lock) $this->free();
return $result;
} else return NULL;
}
public function clean () {
$this->lock();
shm_remove($this->shm);
$this->free();
sem_remove($this->sem);
$this->shm = NULL;
$this->sem = NULL;
}
private function getSem () {
if ($this->sem === NULL) {
$this->sem = sem_get(self::getIdentifier($this->identFile, $this->projId));
}
return $this->sem;
}
private function lock () {
return sem_acquire($this->getSem());
}
private function free () {
return sem_release($this->getSem());
}
private function getShm () {
if ($this->shm === NULL) {
$this->shm = shm_attach(self::getIdentifier($this->identFile, $this->projId), $this->size);
}
return $this->shm;
}
}
我现在有另一个使用这个共享内存类的类,需要对一个变量执行“获取、修改和写入”操作。基本上,这是:
function getModifyWrite () {
$var = $mySHM->get('var');
$var += 42;
$mySHM->set('var', $var);
}
现在的方式,这将锁定信号量,释放它,再次锁定它,然后释放它。我很想一直用信号量锁执行代码。
以前,我的代码由一sem_acquire
对包围sem_release
。不幸的是(感谢@Ben),System V 二进制信号量阻塞lock
了同一进程的其他调用。
PHP 中也没有监视器(实际上可以解决它),而且我不太热衷于使用一些共享内存变量(我想我也可以这样做......)加上传统的信号量来自己实现它们. 我需要独占访问,所以非二进制信号量也不是一个选项。
在不违反 DRY 原则的情况下,对如何执行此操作有任何建议吗?
原始问题
只是一个关于 System V 信号量如何工作以及 PHP 如何使用它们的快速问题:
sem_acquire
如果我在一个进程中多次锁定( )一个信号量,信号量值实际上是否随着每次调用而增加(所以我需要sem_release
像锁定它一样频繁地释放()它),或者做额外的调用sem_acquire
只是继续而不计数如果进程已经拥有信号量(所以第一个free
总是解锁信号量)?
如有疑问,提示如何合理测试就足够了^^
例子:
$sem = sem_get(ftok('/some/file', 'a'));
function doSomething1 () {
sem_acquire($sem);
doSomething2();
// do something else
sem_release($sem);
}
function doSomething2 () {
sem_acquire($sem);
// do stuff
sem_release($sem);
}
在上面的代码中,如果我调用doSomething1
,sem_release
内部是否doSomething2
已经为其他进程释放信号量,或者信号量计数器实际上是否设置为“2”(即使它只有一个容量,因为在 中没有指定其他内容sem_get
)和信号量保持锁定直到第二次释放?
显然,我需要它保持锁定状态doSOmething1
,直到完成工作。当然,我可以直接复制 的内容doSomething2
,但这违反了 DRY 原则,我想避免它。当然,我也可以将工作打包doSOmething2
在一个私有函数中,并从其他两个函数中调用该函数,但这也是额外的,可能是不必要的开销 - 所以我在做之前先问一下。而且,当然³,真正的事情并不是那么简单。
我确实知道信号量一般是如何工作的,但是由于有多种实现策略,我想确保 System V 信号量按照我期望的方式工作(也就是说,增加计数器并要求尽可能多的free
调用接lock
电话)。