有,代码很简单:
$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 "子进程退出";
网友回复