澄清一下上面所说的“进程”的意思
在 Apache Web 服务器上,许多 PHP 请求将在同一个进程空间中执行,因为它具有多线程功能。但是,脚本获取和获取的任何信号量,如果没有释放和删除,PHP 解释器在每次脚本终止时仍然会自动清理它们。
在发送电子邮件之前删除任何垃圾!
(PHP 4, PHP 5, PHP 7, PHP 8)
sem_acquire — 获取信号量
sem_acquire() 默认情况下会阻塞(如有必要),直到可以获取信号量。尝试获取它已经获取的信号量的进程,如果获取信号量会导致其信号量最大数量超出,则会永远阻塞。
处理请求后,进程获取但未显式释放的任何信号量将自动释放,并会生成警告。
澄清一下上面所说的“进程”的意思
在 Apache Web 服务器上,许多 PHP 请求将在同一个进程空间中执行,因为它具有多线程功能。但是,脚本获取和获取的任何信号量,如果没有释放和删除,PHP 解释器在每次脚本终止时仍然会自动清理它们。
在发送电子邮件之前删除任何垃圾!
sem_acquire() 是阻塞的,这意味着使用相同信号量的后续调用将无限期地阻塞,直到信号量被释放。这确保了序列化,但如果您只想检查是否应该继续,则它不太实用。不幸的是,PHP 还没有支持任何以非阻塞方式查询信号量状态的方法。
使用共享内存(shm_ 函数)手动构建这样的机制似乎是可能的。但是,请注意,这并非易事,最终也无济于事。例如,您不能简单地选择一个共享内存变量,存储信号量键并查询它。这样的操作是非事务性和非原子的,即两个或多个并行进程有可能在其中一个设法将其标记为“已锁定”之前,都从共享内存变量中读取“未锁定”。您必须使用(阻塞)信号量来序列化对共享内存变量的访问,从而重新创建您试图解决的问题。
换句话说,如果非阻塞查询对您至关重要,则您需要请求 PHP 设计师解决此问题,或者选择其他机制来执行锁定,一种已经具有此功能的机制。
请注意,当您重置 $sem_identifier 时,信号量将不再阻塞!
此代码不起作用
$key = ftok(__FILE__,'m');
$a = sem_get($key);
sem_acquire($a);
$a = false;
而这个有效
$key = ftok(__FILE__,'m');
$a = sem_get($key);
sem_acquire($a);
//$a = false;
所以:为您的标识符使用唯一的变量名!
我最近不得不使用信号量来避免同时运行数据库更新。
为了测试它,我在同一台桌面上使用了两个浏览器窗口(Chrome)。
在这种情况下,信号量没有按预期工作。
同样,文件上的 flock 也没有按预期工作。
从命令行运行脚本按预期工作。
一旦我使用了两个不同的浏览器,一个在我的桌面上,另一个在虚拟机中,它就可以按预期工作了。
希望这可以帮助任何人避免在此主题上进行奇怪的调试会话……
如果您需要非阻塞信号量,以下是如何实现它的示例。使用共享内存变量来标记是否存在锁,然后在针对该变量的操作周围使用信号量。我将我的共享变量称为“token”。
<?php
echo '<pre>';
$resourceSemaphore = sem_get(7);
$tokenSemaphore = sem_get(8);
$tokenValue = shm_attach(9, 100);
function myEcho($v) {
echo microtime() . ' ' . $v . "\n";
}
// sem_remove($resourceSemaphore);
// sem_remove($tokenSemaphore);
// exit();
function try_lock() {
global $resourceSemaphore, $tokenSemaphore, $tokenValue;
myEcho('begin try_lock()');
myEcho('acquire token semaphore');
sem_acquire($tokenSemaphore);
myEcho(' token semaphore acquired');
$tmp = shm_get_var($tokenValue, 6);
myEcho(' token value: ' . var_export($tmp, true));
$exit = $tmp;
if (!$exit) {
$tmp = shm_put_var($tokenValue, 6, true);
$tmp = shm_get_var($tokenValue, 6);
myEcho(' token new value: ' . var_export($tmp, true));
}
myEcho('release token semaphore');
sem_release($tokenSemaphore);
if ($exit) return false;
myEcho('acquire resource semaphore');
sem_acquire($resourceSemaphore);
myEcho(' resource semaphore acquired');
return true;
}
function release() {
global $resourceSemaphore, $tokenSemaphore, $tokenValue;
myEcho('release resource semaphore');
sem_release($resourceSemaphore);
myEcho('acquire token semaphore');
sem_acquire($tokenSemaphore);
myEcho(' token semaphore acquired');
$tmp = shm_get_var($tokenValue, 6);
myEcho(' token value: ' . var_export($tmp, true));
$tmp = shm_put_var($tokenValue, 6, false);
$tmp = shm_get_var($tokenValue, 6);
myEcho(' token new value: ' . var_export($tmp, true));
myEcho('release token semaphore');
sem_release($tokenSemaphore);
}
for ($triesLeft = 5; $triesLeft > 0 && !try_lock(); $triesLeft--) {
myEcho('failed to acquire resource');
myEcho('wait for 1 sec');
sleep(1);
myEcho('try again');
}
myEcho(' access the resource for 4 sec');
//粘贴此处你的代码,访问你的资源
sleep(4);
release();
myEcho('the end');
?>
当我并行执行此脚本两个实例时,我得到以下输出
-------(第一个实例)----------------------------------------
... 482 begin try_lock()
... 482 acquire token semaphore
... 482 token semaphore acquired
... 482 token value: false
... 482 token new value: true
... 482 release token semaphore
... 482 acquire resource semaphore
... 482 resource semaphore acquired
... 482 access the resource for 4 sec
... 486 release resource semaphore
... 486 acquire token semaphore
... 486 token semaphore acquired
... 486 token value: true
... 486 token new value: false
... 486 release token semaphore
... 486 the end
-------(第二个实例)----------------------------------------
... 485 begin try_lock()
... 485 acquire token semaphore
... 485 token semaphore acquired
... 485 token value: true
... 485 release token semaphore
... 485 failed to acquire resource
... 485 wait for 1 sec
...
... 486 wait for 1 sec
... 487 try again
... 487 begin try_lock()
... 487 acquire token semaphore
... 487 token semaphore acquired
... 487 token value: false
... 487 token new value: true
... 487 release token semaphore
... 487 acquire resource semaphore
... 487 resource semaphore acquired
... 487 access the resource for 4 sec
... 491 release resource semaphore
... 491 acquire token semaphore
... 491 token semaphore acquired
... 491 token value: true
... 491 token new value: false
... 491 release token semaphore
... 491 the end
不幸的是,PHP 目前不支持非阻塞信号量。
如果需要类似的功能,您可以将信号量与共享内存一起使用来创建自己的非阻塞锁机制。
使用共享内存变量来标记是否存在锁,然后在对该变量的操作周围使用信号量。