我正在尝试更新APC中的变量,并且将尝试执行此操作的许多进程.
APC不提供锁定功能,所以我正在考虑使用其他机制......到目前为止我发现的是mysql的GET_LOCK()和php的flock().还有什么值得考虑的吗?
更新:我找到了sem_acquire,但它似乎是一个阻塞锁.
/* CLASS ExclusiveLock Description ================================================================== This is a pseudo implementation of mutex since php does not have any thread synchronization objects This class uses flock() as a base to provide locking functionality. Lock will be released in following cases 1 - user calls unlock 2 - when this lock object gets deleted 3 - when request or script ends ================================================================== Usage: //get the lock $lock = new ExclusiveLock( "mylock" ); //lock if( $lock->lock( ) == FALSE ) error("Locking failed"); //-- //Do your work here //-- //unlock $lock->unlock(); =================================================================== */ class ExclusiveLock { protected $key = null; //user given value protected $file = null; //resource to lock protected $own = FALSE; //have we locked resource function __construct( $key ) { $this->key = $key; //create a new resource or get exisitng with same key $this->file = fopen("$key.lockfile", 'w+'); } function __destruct() { if( $this->own == TRUE ) $this->unlock( ); } function lock( ) { if( !flock($this->file, LOCK_EX | LOCK_NB)) { //failed $key = $this->key; error_log("ExclusiveLock::acquire_lock FAILED to acquire lock [$key]"); return FALSE; } ftruncate($this->file, 0); // truncate file //write something to just help debugging fwrite( $this->file, "Locked\n"); fflush( $this->file ); $this->own = TRUE; return TRUE; // success } function unlock( ) { $key = $this->key; if( $this->own == TRUE ) { if( !flock($this->file, LOCK_UN) ) { //failed error_log("ExclusiveLock::lock FAILED to release lock [$key]"); return FALSE; } ftruncate($this->file, 0); // truncate file //write something to just help debugging fwrite( $this->file, "Unlocked\n"); fflush( $this->file ); $this->own = FALSE; } else { error_log("ExclusiveLock::unlock called on [$key] but its not acquired by caller"); } return TRUE; // success } };
您可以使用apc_add函数来实现此目的,而无需使用文件系统或mysql. apc_add
仅在变量尚未存储时才成功; 因此,提供了一种锁定机制.TTL可用于确保愚蠢的锁定器不会永远保持锁定状态.
原因apc_add
是正确的解决方案是因为它避免了在检查锁定并将其设置为"由您锁定"之间存在的竞争条件.由于apc_add
只设置了尚未设置的值(将其"添加"到缓存中),因此它确保一次两次调用无法获取锁定,无论它们是否及时接近.没有同时检查和设置锁定的解决方案本身就会受到这种竞争条件的影响; 在没有竞争条件的情况下成功锁定需要一个原子操作.
由于APC锁定仅存在于php执行的上下文中,因此它可能不是一般锁定的最佳解决方案,因为它不支持主机之间的锁定. Memcache
还提供了一个原子添加函数,因此也可以用于这种技术 - 这是一种在主机之间进行锁定的方法. Redis
还支持原子'SETNX'函数和TTL,是一种非常常见的主机锁定和同步方法.然而,OP特别要求为APC提供解决方案.
如果锁定点是为了防止多个进程尝试填充空缓存键,为什么不想要阻塞锁?
$value = apc_fetch($KEY); if ($value === FALSE) { shm_acquire($SEMAPHORE); $recheck_value = apc_fetch($KEY); if ($recheck_value !== FALSE) { $new_value = expensive_operation(); apc_store($KEY, $new_value); $value = $new_value; } else { $value = $recheck_value; } shm_release($SEMAPHORE); }
如果缓存是好的,你只需滚动它.如果缓存中没有任何内容,则会获得锁定.一旦你有锁,你需要仔细检查缓存,以确保在等待锁定时,缓存没有重新填充.如果缓存已重新填充,请使用该值并释放锁定,否则,执行计算,填充缓存然后释放锁定.