hokoo/wp-lock

WordPress 核心锁和互斥锁的库

v1.1 2024-05-05 19:37 UTC

This package is auto-updated.

Last update: 2024-09-06 12:24:48 UTC


README

PHPUnit Tests

这之前是来自原始仓库 soulseekah/wp-lock 的分支,由 Gennady Kovshenin 创建。现在这里有一个独立的仓库,其中包括以下更改:

  • 使用自定义数据库表作为 WPDB 锁的存储,允许以简单方便的方式管理锁。
  • 快速:获取一个锁只需一个查询,而不是11个或更多。
  • 可读性:代码具有良好的文档,易于理解。

WP Lock 的前提条件

考虑以下用户余额充值函数,该函数容易受到竞争条件的影响

// Top-up function that is not thread-safe
public function topup_user_balance( $user_id, $topup ) {
	$balance = get_user_meta( $user_id, 'balance', true );
	$balance = $balance + $topup;
	update_user_meta( $user_id, 'balance', $balance );
	return $balance;
}

尝试在16个线程中调用上述代码100次。余额将低于预期。

下面的代码是线程安全的。

// A thread-safe version of the above topup function.
public function topup_user_balance( $user_id, $topup ) {
	$user_balance_lock = new WP_Lock( "$user_id:meta:balance" );
	$user_balance_lock->acquire( WP_Lock::WRITE );

	$balance = get_user_meta( $user_id, 'balance', true );
	$balance = $balance + $topup;
	update_user_meta( $user_id, 'balance', $balance );

	$user_balance_lock->release();

	return $balance;
}

用法

通过 Composer 在您的插件中安装 composer require hokoo/wp-lock

不要忘记在您的插件中包含 Composer 自动加载器并声明使用 WP_Lock 类。

use iTRON\WP_Lock;
require 'vendor/autoload.php';

无超时地获取读阻塞锁。

$lock = new WP_Lock\WP_Lock( 'my-lock' );

$lock->acquire( WP_Lock::READ, true, 0 );
// do something, and then
$lock->release();
public function acquire( $level = self::WRITE, $blocking = true, $expiration = 30 )

锁级别

  • WP_Lock::READ - 其他进程可以获取读锁,但不能获取写锁,直到原始锁被释放。一个共享读锁。
  • WP_Lock::WRITE(默认)- 其他进程不能获取读或写锁,直到原始锁被释放。一个独占读写锁

阻塞策略

  • 阻塞意味着进程将在这里等待,直到获取锁(5微秒的自旋锁)。
  • 非阻塞锁获取不会等待其他锁被释放,但如果锁已被另一个进程获取,则立即返回 false。因此,应该检查成功。
if ( $lock->acquire( WP_Lock::READ, false, 0 ) ) {
    // do something, and then
    $lock->release();
}

超时策略

  • 0秒超时意味着锁应该被释放,否则将在 php 进程完成后自动释放。
  • 非零超时意味着锁可能不会手动释放,然后将在指定秒数后自动释放。

锁存在检查

您还可能想要检查是否有其他进程获取了锁,而实际上并没有尝试获取它。

$another_lock = new WP_Lock\WP_Lock( 'my-lock' );

if ( $another_lock->acquire( WP_Lock::READ, false, 0 ) ) {
    $lock->lock_exists( WP_Lock::READ ); // true
    $lock->lock_exists( WP_Lock::WRITE ); // false
    
    $another_lock->release();
}

if ( $another_lock->acquire( WP_Lock::WRITE, false, 0 ) ) {
    $lock->lock_exists( WP_Lock::READ ); // true
    $lock->lock_exists( WP_Lock::WRITE ); // true
    
    $another_lock->release();
}

注意事项

在高并发设置中,您可能会从 MySQL 获取死锁错误。这是正常的。库可以优雅地处理这些错误并按需重试查询。