如何正确使用PHP5信号量?
How to properly use PHP5 semaphores?
我有这个函数试图从缓存中读取一些值。但如果值不存在,它应该调用替代源 API 并将新值保存到缓存中。但是,服务器非常过载,几乎每次当值不存在时都会创建一个请求(很多 API 调用)并且每个请求都会将新值存储到缓存中。但是,我想要的是能够多次调用API,但只有一个process/request能够将其存储在缓存中:
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 a.k.a。 SYSVSEM_SEM
初始化为 sem_get
的 $max_acquire
并随着进程获取它而递减。
调用 sem_get
的第一个进程获取 SYSVSEM_USAGE
信号量的值(见下文)。对于第一个进程,它等于 1
,因为 semget
之后具有原子 semop
函数的扩展 sets it to 1
。如果这真的是第一个进程,扩展程序会将 SYSVSEM_SEM
信号量值分配给 $max_acquire
.
信号量 1 a.k.a。 SYSVSEM_USAGE
使用信号量的进程数。
信号量 2 a.k.a。 SYSVSEM_SETVAL
对内部 SETVAL
和 GETVAL
操作起到锁的作用(参见 man 2 semctl
)。例如,它设置为 1
,而扩展设置 SYSVSEM_SEM
为 $max_acquire
,然后重置为零。
最后,sem_get
将一个结构(包含信号集 ID、密钥和其他信息)包装到 PHP 资源中并 returns 它。
所以你应该在一些初始化过程中调用它,当你只准备使用信号量时。
sem_acquire()
这里是 $max_acquire
goes into play.
SYSVSEM_SEM
的值(我们称之为 semval
)最初等于 $max_acquire
。 semop()
阻塞,直到 semval
变得大于或等于 1
。然后semval
减去1
.
如果$max_acquire = 1
,则semval
在第一次调用后变为零,接下来对sem_acquire()
的调用将阻塞,直到semval
被[=16=恢复] 打电话。
当您需要从可用集合 ($max_acquire
) 中获取下一个“锁”时调用它。
sem_release()
与 sem_acquire()
几乎相同,只是它增加了 SYSVSEM_SEM
的值。
当您不再需要之前使用 sem_acquire()
获得的“锁”时调用它。
sem_remove()
立即删除信号量集,唤醒在该集 semop
中阻塞的所有进程(来自 IPC_RMID
部分,SEMCTL(2) 手册页)。
所以这实际上与使用 ipcrm
命令删除信号量相同。
文件权限应该是 0666,而不是 6000,因为您正在尝试执行此操作。
我有这个函数试图从缓存中读取一些值。但如果值不存在,它应该调用替代源 API 并将新值保存到缓存中。但是,服务器非常过载,几乎每次当值不存在时都会创建一个请求(很多 API 调用)并且每个请求都会将新值存储到缓存中。但是,我想要的是能够多次调用API,但只有一个process/request能够将其存储在缓存中:
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 a.k.a。 SYSVSEM_SEM
初始化为 sem_get
的 $max_acquire
并随着进程获取它而递减。
调用 sem_get
的第一个进程获取 SYSVSEM_USAGE
信号量的值(见下文)。对于第一个进程,它等于 1
,因为 semget
之后具有原子 semop
函数的扩展 sets it to 1
。如果这真的是第一个进程,扩展程序会将 SYSVSEM_SEM
信号量值分配给 $max_acquire
.
信号量 1 a.k.a。 SYSVSEM_USAGE
使用信号量的进程数。
信号量 2 a.k.a。 SYSVSEM_SETVAL
对内部 SETVAL
和 GETVAL
操作起到锁的作用(参见 man 2 semctl
)。例如,它设置为 1
,而扩展设置 SYSVSEM_SEM
为 $max_acquire
,然后重置为零。
最后,sem_get
将一个结构(包含信号集 ID、密钥和其他信息)包装到 PHP 资源中并 returns 它。
所以你应该在一些初始化过程中调用它,当你只准备使用信号量时。
sem_acquire()
这里是 $max_acquire
goes into play.
SYSVSEM_SEM
的值(我们称之为 semval
)最初等于 $max_acquire
。 semop()
阻塞,直到 semval
变得大于或等于 1
。然后semval
减去1
.
如果$max_acquire = 1
,则semval
在第一次调用后变为零,接下来对sem_acquire()
的调用将阻塞,直到semval
被[=16=恢复] 打电话。
当您需要从可用集合 ($max_acquire
) 中获取下一个“锁”时调用它。
sem_release()
与 sem_acquire()
几乎相同,只是它增加了 SYSVSEM_SEM
的值。
当您不再需要之前使用 sem_acquire()
获得的“锁”时调用它。
sem_remove()
立即删除信号量集,唤醒在该集 semop
中阻塞的所有进程(来自 IPC_RMID
部分,SEMCTL(2) 手册页)。
所以这实际上与使用 ipcrm
命令删除信号量相同。
文件权限应该是 0666,而不是 6000,因为您正在尝试执行此操作。