+
95
-

回答

有,代码很简单:

$lock = new \Swoole\Lock(SWOOLE_MUTEX); // 参数是锁的种类, 下面细说
$lock->lock();//加锁
$lock->unlock();//解锁

注意:

1、在协程中无法使用锁,请谨慎使用,不要在lock和unlock操作中间使用可能引起协程切换的API。

2、请勿在onReceive等回调函数中创建锁,否则底层的GlobalMemory内存会持续增长,造成内存泄漏。

3、避免出现死锁,以下两种情况会出现死锁,线程试图对同一个互斥量A加锁两次,线程1拥有A锁,请求获得B锁;线程2拥有B锁,请求获得A锁。

4、阻塞锁和非阻塞锁

$lock->lock(); // 阻塞, 直到成功申请到锁

$lock->trylock(); // 不会阻塞, 立即返回抢锁的结果

因为锁的规则不同, 信号量 SWOOLE_SEM 不支持 trylock() 这样非阻塞调用方式

swoole锁的种类

swoole 有五种类型的锁

互斥锁 SWOOLE_MUTEX

自旋锁 SWOOLE_SPINLOCK

读写锁 SWOOLE_RWLOCK

文件锁 SWOOLE_FILELOCK

信号量 SWOOLE_SEM

互斥锁 SWOOLE_MUTEX

这个概念理解起来最容易: 互斥锁 = 独占锁 = 排它锁 = 写锁 .

锁的规则: 顾名思义, 就是这个锁当前只能有一个 进程/线程 使用, 其他 进程/线程 只能等待

实现: swoole 底层使用 pthread_mutex_xxx 系列函数实现.

场景: 任何需要 排他/独占 的场景都适用

自旋锁 SWOOLE_SPINLOCK

自旋锁和互斥锁类似, 只是在 阻塞 时的状态不一样:

互斥锁没有申请到锁时, 通常会进入睡眠状态, 这样当锁释放时, 就需要唤醒 ; 自旋锁则会一直循环在那里等待锁

实现: swoole 底层使用 pthread_spin_xxx 系列函数实现.

场景: 锁保持时间非常短的情况, 减少 睡眠 -> 唤醒 的成本

读写锁 SWOOLE_RWLOCK

$lock->lock(); // 写锁

$lock->lock_read(); // 读锁

写锁上面已经见过了, 读写锁由 读锁 + 写锁 组成

锁的规则: 如果当前有 读锁 , 可以继续申请 读锁 , 但是不能申请 写锁 ; 写锁 的规则和上面一致

实现: swoole 底层使用 pthread_rwlock_xxx 系列函数实现.

场景: 读多写少的场景, 减少 锁 的阻塞带来的性能损耗

文件锁 SWOOLE_FILELOCK

除了使用文件作为锁, 规则和场景和 读写锁 SWOOLE_RWLOCK 一致

实现: swoole 底层使用 fcntl() 函数实现.

信号量 SWOOLE_SEM

信号量的规则稍微会复杂一些, 会涉及到 2 个操作: P V

锁的规则: 如果信号量 >= 0, 则可以执行 P 操作, 并将信号量减一; 如果信号量 < 最大值, 则可以执行 V 操作, 并将信号量加一

实现: swoole 底层调用 <sys/sem.h> 实现.

场景: 信号量操作可以想象成 仓库进货出货 的过程, 仓库是空的, 就不能用 P 操作来出货了, 仓库是满的, 就不能用 V 操作来继续进货了

注意: 信号量没有 trylock() 方法; swoole 没有提供 容量 的设置, 退化为容量为 1 的情况了, 其实和 互斥锁 SWOOLE_MUTEX 差不多了

示例代码:

//创建锁对象
$lock = new swoole_lock(SWOOLE_MUTEX); //互斥锁
echo "创建互斥锁";
$lock->lock(); //开始锁定 主进程
if(pcntl_fork() >0 ){
sleep(1);
$lock->unlock(); //解锁
}else{
echo "子进程 等待锁 ";
$lock->lock(); //上锁
echo "子进程 获取锁";
$lock->unlock(); //释放锁
exit("子进程退出");
}

echo "主进程 释放锁";
unset($lock);
sleep(1);
echo "子进程退出";


网友回复

我知道答案,我要回答