我有这个函数试图从缓存中读取一些值。但是如果值不存在,它应该调用备用源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中正确使用信号量吗?
答案 0 :(得分:3)
fetch_cache
功能中创建和删除信号量。将sem_get()
放入初始化方法(例如__construct
)。sem_remove()
删除信号量,但需要使用清除方法(例如__destruct
)。或者,您可能希望将它们保持更长时间 - 取决于应用程序的逻辑。sem_acquire()
获取锁定,使用sem_release()
释放锁定。sem_get()
创建一组three信号量。
底层C函数semget
不是原子。当两个进程尝试调用semget
时,有可能race condition。因此,应在某些初始化过程中调用semget
。 PHP扩展通过三个信号量克服了这个问题:
信号量0 a.k.a. SYSVSEM_SEM
初始化为sem_get
' s $max_acquire
并在进程获取时递减。
调用sem_get
的第一个进程获取SYSVSEM_USAGE
信号量的值(见下文)。对于第一个流程,它等于1
,因为具有原子semop
的扩展sets it to 1
在semget
之后正常运行。如果这确实是第一个过程,则扩展程序会将SYSVSEM_SEM
信号量值分配给$max_acquire
。
信号量1 a.k.a. SYSVSEM_USAGE
使用信号量的进程数。
Semaphore 2 a.k.a. 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
大于或等于1
。然后从semval
中减去$max_acquire = 1
。
如果semval
,那么sem_acquire()
在第一次通话后变为零,并且下一次拨打semval
填空,直到通过sem_release()
通话恢复$max_acquire
当你需要获得下一个"锁定"从可用集(sem_release()
)。
sem_acquire()
与SYSVSEM_SEM
几乎相同,只是增加sem_acquire()
的值。
当您不再需要"锁定"先前使用sem_remove()
获得。
semop
立即删除信号量集,唤醒集合中IPC_RMID
中阻止的所有进程(来自ipcrm
部分, SEMCTL(2)手册页。)
所以这与使用<div class="contact-label span11 rights">
<div class="row-fluid">
<div class="span3">
<input type="checkbox" class="input-text" value="Y" name="contactpersonen_canorder_0" id="contactpersonen_canorder_0"></input>
<label class="checkbox" for="contactpersonen_canorder_0">Mag border plaatsen</label>
<input type="checkbox" class="input-text" value="Y" name="contactpersonen_canseestock_0" id="contactpersonen_canseestock_0"></input>
<label class="checkbox" for="contactpersonen_canseestock_0">Mag gderen afhalen</label>
</div>
<div class="span3">
<input type="checkbox" class="input-text" value="Y" name="contactpersonen_canseeorders_0" id="contactpersonen_canseeorders_0"></input>
<label class="checkbox" for="contactpersonen_canseeorders_0">Mag orders inzien</label>
<input type="checkbox" class="input-text" value="Y" name="contactpersonen_canaddaddress_0" id="contactpersonen_canaddaddress_0"></input>
<label class="checkbox" for="contactpersonen_canaddaddress_0">Mag bezorgadressen aanpassen</label>
</div>
<div class="span3">
<input type="checkbox" class="input-text" value="Y" name="contactpersonen_canseeprice_0" id="contactpersonen_canseeprice_0"></input>
<label class="checkbox" for="contactpersonen_canseeprice_0">Mag netto prijs zien</label>
<input type="checkbox" class="input-text" value="Y" name="contactpersonen_canseecredit_0" id="contactpersonen_canseecredit_0"></input>
<label class="checkbox" for="contactpersonen_canseecredit_0">Mag facturen en creditfacturen inzien</label>
</div>
<div class="span3">
<input type="checkbox" class="input-text" value="Y" name="contactpersonen_canseepickup_0" id="contactpersonen_canseepickup_0"></input>
<label class="checkbox" for="contactpersonen_canseepickup_0">Mag wijzigingen doorgeven</label>
</div>
</div>
</div>
命令删除信号量实际上相同。
答案 1 :(得分:0)
您要执行的操作的文件权限应为 0666 ,而不是 6000 。