如何正确使用 PHP5 信号量?

clz*_*ola 7 php semaphore

我有这个函数试图从缓存中读取一些值。但是如果值不存在,它应该调用替代源 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;
}
Run Code Online (Sandbox Code Playgroud)

这是在 PHP5 中正确使用信号量吗?

Rus*_*nov 8

简答

  1. 您不需要在fetch_cache函数内创建和删除信号量。放入sem_get()初始化方法(如__construct)。
  2. 您应该使用sem_remove(), 但在清理方法(例如__destruct)中删除信号量。或者,您可能希望将它们保留更长时间 - 取决于您的应用程序的逻辑。
  3. 使用sem_acquire()获取锁,并sem_release()释放他们。

描述

sem_get()

创建一组三个信号量。

底层的C函数semget不是原子。还有的可能性竞争条件,当两个进程试图调用semget。因此,semget应该在一些初始化过程中调用。PHP 扩展通过三个信号量克服了这个问题:

信号量 0 又名 SYSVSEM_SEM

初始化为sem_get's$max_acquire并随着进程获取它而递减。

调用的第一个进程sem_get获取SYSVSEM_USAGE信号量的值(见下文)。对于第一个进程,它等于1,因为扩展将它设置为1紧跟semop在 之后的原子函数semget。如果这确实是第一个进程,则扩展将SYSVSEM_SEM信号量值分配给$max_acquire

信号量 1 又名 SYSVSEM_USAGE

使用信号量的进程数。

信号量 2 又名 SYSVSEM_SETVAL

对内部SETVALGETVAL操作起到锁的作用(请参阅 参考资料man 2 semctl)。例如,它被设定为1当该扩展程序集SYSVSEM_SEM$max_acquire,然后被重置回零。

最后,sem_get将一个结构体(包含信号量集合ID、key等信息)包装成一个PHP资源并返回。

所以你应该在一些初始化过程中调用它,当你只准备使用信号量时。

sem_acquire()

这是$max_acquire 发挥作用的地方

SYSVSEM_SEM的值(我们称之为semval)最初等于$max_acquiresemop()阻塞直到semval变得大于或等于1。然后1从 中减去semval

如果$max_acquire = 1,则semval在第一次调用后变为零,下一次调用sem_acquire()将阻塞,直到semvalsem_release()调用恢复。

当您需要从可用集合 ( $max_acquire) 中获取下一个“锁”时调用它。

sem_release()

与 几乎相同sem_acquire(),只是它增加了SYSVSEM_SEM的值。

当您不再需要之前使用sem_acquire().

sem_remove()

立即删除信号集,唤醒allprocesses阻塞在semop该组(从IPC_RMID了semctl(2)手册页)。

所以这实际上与使用ipcrm命令删除信号量相同。