【问题标题】:How to properly use PHP5 semaphores?如何正确使用 PHP5 信号量?
【发布时间】:2016-10-11 12:02:13
【问题描述】:

我有这个函数试图从缓存中读取一些值。但是如果值不存在,它应该调用替代源 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 中信号量的正确使用吗?

【问题讨论】:

  • 在redis中使用事务来设置key会不会更合适?即在r​​edis中启动事务-检查是否未设置密钥-如果未设置则返回,否则返回。类似的效果,但保持 PHP 简单?
  • 这确实是个好建议。

标签: php semaphore


【解决方案1】:

简答

  1. 您不需要在fetch_cache 函数中创建和删除信号量。将sem_get()放入初始化方法中(如__construct)。
  2. 您应该使用sem_remove() 删除信号量,但要使用清理方法(例如__destruct)。或者,您可能希望将它们保留更长时间 - 取决于您的应用程序的逻辑。
  3. 使用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,因为在semget 之后具有原子semop 的扩展sets it to 1 函数。如果这确实是第一个进程,则扩展程序会将SYSVSEM_SEM 信号量值分配给$max_acquire

信号量 1 又名 SYSVSEM_USAGE

使用信号量的进程数。

信号量 2 又名 SYSVSEM_SETVAL

对内部SETVALGETVAL 操作起到锁定作用(请参阅man 2 semctl)。例如,它设置为1,而扩展设置SYSVSEM_SEM$max_acquire,然后重置为零。

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

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

sem_acquire()

这是$max_acquire goes into play 的位置。

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

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

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

sem_release()

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

当您不再需要之前通过sem_acquire() 获得的“锁”时调用它。

sem_remove()

立即删除信号量集,唤醒在集上semop 中阻塞的所有进程(来自IPC_RMID 部分,SEMCTL(2) 手册页)。

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

【讨论】:

    【解决方案2】:

    对于您要执行的操作,文件权限应为 0666 而不是 6000

    【讨论】:

      猜你喜欢
      • 2021-01-17
      • 1970-01-01
      • 2010-11-11
      • 1970-01-01
      • 2011-02-07
      • 2019-06-21
      • 1970-01-01
      • 2021-02-02
      • 1970-01-01
      相关资源
      最近更新 更多