我有这个功能,试图从缓存中读取一些值.但是,如果值不存在,则应调用备用源API并将新值保存到缓存中.但是,服务器非常过载,几乎每次不存在值时,就会创建一个请求(许多API调用),并且每个请求都会将新值存储到缓存中.但是,我想要的是能够多次调用API,但是只有一个进程/请求能够将其存储在缓存中:
function fetch_cache($key, $alternativeSource) {
$redis = new Redis();
$redis->pconnect(ENV_REDIS_HOST);
$value = $redis->get($key);
if( $value === NULL ) {
$value = file_get_contents($alternativeSource);
// here goes part that I need help with
$semaphore = sem_get(6000, 1); // does this need to be called each time this function is called?
if( $semaphore === FALSE ) {
// This means I have failed to create semaphore?
}
if( sem_aquire($semaphore, true) ) {
// we have aquired semaphore so here
$redis->set($key, $value);
sem_release($semaphore); // releasing lock
}
// This must be call because I have called sem_get()?
sem_remove($semaphore);
}
return $value;
}
PHP5是否正确使用了信号量?
解决方法:
简短答案
>您无需在fetch_cache函数中创建和删除信号量.将sem_get()放入初始化方法(例如__construct)中.
>您应该使用sem_remove()删除信号量,但是要使用清理方法(例如__destruct).或者,您可能希望将它们保留更长的时间-取决于应用程序的逻辑.
>使用sem_acquire()获取锁,然后使用sem_release()释放锁.
描述
sem_get()
创建一组three信号量.
底层的C函数semget不是原子的.当两个进程尝试调用semget时,可能会出现race condition.因此,应在某些初始化过程中调用semget. PHP扩展通过三个信号量克服了这个问题:
信号量0亦称SYSVSEM_SEM
初始化为sem_get的$max_acquire,并在进程获取时递减.
第一个调用sem_get的进程将获取SYSVSEM_USAGEsemaphore的值(请参见下文).对于第一个过程,它等于1,因为扩展名sets it to 1
在semget之后具有原子semop函数.并且,如果这确实是第一个过程,则扩展会将SYSVSEM_SEM信号量值分配给$max_acquire.
信号灯1又称SYSVSEM_USAGE
使用信号量的进程数.
信号量2又称SYSVSEM_SETVAL
充当内部SETVAL和GETVAL操作的锁(请参见man 2 semctl).例如,将其设置为1,而扩展名将SYSVSEM_SEM设置为$max_acquire,然后将其重置为零.
最后,sem_get将一个结构(包含信号量集ID,键和其他信息)包装到PHP资源中并返回它.
因此,当您仅准备使用信号量时,应该在一些初始化过程中调用它.
sem_acquire()
这是$max_acquire goes into play的位置.
SYSVSEM_SEM的值(我们称其为semval)最初等于$max_acquire. semop()阻塞,直到semval大于或等于1.然后从semval中减去1.
如果$max_acquire = 1,则在第一次调用之后semval变为零,并且对sem_acquire()的下一次调用将填充块,直到通过sem_release()调用恢复semval.
需要从可用集中($max_acquire)获取下一个“锁”时调用它.
sem_release()
与sem_acquire()几乎一样,只是它增加了SYSVSEM_SEM的值.
当您不再需要以前通过sem_acquire()获取的“锁”时调用它.
sem_remove()
立即删除信号量集,唤醒该集中的semop中阻塞的所有进程(来自IPC_RMID部分,SEMCTL(2)手册页).
因此,这实际上与使用ipcrm命令删除信号量相同.