二十三、Swoole 基础学习笔记 - Swoole 进程间锁 Lock

作者: 温新

分类: 【Swoole 系列】

阅读: 683

时间: 2023-03-13 11:49:30

hi,我是温新,一名PHPer

文章基于 Swoole 5.0.1 版本编写。

学习目标:学习并了解 Lock 的使用

说明:本篇文章结合官方文档编写及参考网络资料编写,虽非全部原创,但也是结合了自己的理解,若转载请附带本文 URL,编写不易,持续编写更不易,谢谢!

说起锁,PHP 中有锁,MySQL 中有锁...锁在多进程、多线程操作中有着至关重要的作用。比如有两个进程同时要修改 $num=1 的值,进程一要修改为 5,进程二要修改为10,那么$num 最后的值会是多少?这个结果无法确定。可以通过加锁来确保同时只有一个进程对资源进程操作,操作完成后才有由其他进程操作。

Swoole Lock

PHP 代码中可以很方便地创建一个锁,用来实现数据同步。Lock 类支持 5 种锁的类型:

<span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important"><?</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">php</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">// 23-swoole-lock.php</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box"></span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$lock</span> <span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">=</span> <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">new</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">Swoole\Lock</span>();</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box"></span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$pool</span> <span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">=</span> <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">new</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">Swoole\Process\Pool</span>(<span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">3</span>, <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">SWOOLE_IPC_NONE</span>, <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">0</span>, <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">true</span>);</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box"></span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$pool</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">-></span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">on</span>(<span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">'Workerstart'</span>, <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">function</span> (<span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$pool</span>, <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$workerId</span>) <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">use</span> (<span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$lock</span>) {</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">echo</span> <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">'Worker '</span> . <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$workerId</span> . <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">'执行 '</span> . <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">PHP_EOL</span>;</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">// 加锁</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$lock</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">-></span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">lock</span>();</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">echo</span> <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">'Worker '</span> . <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$workerId</span> . <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">' 获得锁 '</span> . <span style="box-sizing: border-box;color: rgb(232, 191, 106) !important">microtime</span>(<span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">true</span>) . <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">PHP_EOL</span>;</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(232, 191, 106) !important">sleep</span>(<span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">3</span>);</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">// 释放锁</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$lock</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">-></span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">unlock</span>();</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">echo</span> <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">'Worker '</span> . <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$workerId</span> . <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">' 退出 '</span> . <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">PHP_EOL</span>;</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">});</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box"></span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$pool</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">-></span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">start</span>();</span>

代码解析:

  • 在这一段代码中,使用 3 个进程执行任务;
  • 第一个进程进来之后进行加锁,加锁后,其他进程进程会阻塞,知道持有锁的进程执行完成。

执行结果:

<span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$php</span> <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">23</span><span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">-swoole-lock-1</span>.php </span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">Worker <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">0</span> 执行</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">Worker <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">0</span> 获得锁 <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">1673273562</span>.803</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">Worker <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">1</span> 执行</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">Worker <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">2</span> 执行</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">Worker <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">0</span> 退出 </span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">Worker <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">1</span> 获得锁 <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">1673273565</span>.8035</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">Worker <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">0</span> 执行</span>

结果分析:

  • 0 ,Worker 0 进来后,获得锁,Worker 1 和 2 出现阻塞操作;
  • 3 秒后,Worker 0 执行完成,然后 Worker 获得锁,然后其他 Worker 继续阻塞。

Lock 相关方法

__contruct

<span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">/**</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">* @param int $type 锁的类型</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">* @param string $lockfile 指定文件锁的路径【当类型为 SWOOLE_FILELOCK 时必须传入】</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">**/</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">Swoole\Lock</span>::<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">__construct</span>(<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">int</span> <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$type</span> <span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">=</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">SWOOLE_MUTEX</span>, <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">string</span> <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$lockfile</span> <span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">=</span> <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">''</span>);</span>

注意:

1、不要循环创建 / 销毁锁的对象,否则会发生内存泄漏;

2、每一种类型的锁支持的方法都不一样。如读写锁、文件锁可以支持 $lock->lock_read()。另外除文件锁外,其他类型的锁必须在父进程内创建,这样 fork 出的子进程之间才可以互相争抢锁。

lock

含义:加锁操作。如果有其他进程持有锁,那这里将进入阻塞,直到持有锁的进程 unlock() 释放锁。

<span style="color: rgb(0, 176, 240);">Swoole\Lock->lock(): bool</span><br></br>

unlock

含义:释放锁。

<span style="color: rgb(0, 176, 240);">Swoole\Lock->unlock(): bool</span><br></br>

lock_read

含义:只读加锁。

<span style="color: rgb(0, 176, 240);">Swoole\Lock->lock_read(): bool</span><br></br>
  • 在持有读锁的过程中,其他进程依然可以获得读锁,可以继续发生读操作;
  • 但不能 $lock->lock()$lock->trylock(),这两个方法是获取独占锁,在独占锁加锁时,其他进程无法再进行任何操作,包括读锁;
  • 当另一个进程获得独占锁 (调用 $lock->lock()/$lock->trylock()) 时,$lock->lock_read() 会发生阻塞,直到持有独占锁的进程释放锁。

只有 SWOOLE_RWLOCKSWOOLE_FILELOCK 类型的锁支持只读加锁

trylock_read()

含义:加锁。此方法与 lock_read() 相同,但是非阻塞的。

<span style="color: rgb(0, 176, 240);">Swoole\Lock->trylock_read(): bool</span><br></br>

调用会立即返回,必须检测返回值以确定是否拿到了锁。

lockwait

含义:加锁操作。作用与 lock() 方法一致,但 lockwait() 可以设置超时时间。

<span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">/**</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">* @param float $timeout 指定超时时间</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">* </span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">* @return 1、在规定的时间内未获得锁,返回 false</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">*    2、加锁成功返回 true</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">**/</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">Swoole\Lock</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">-></span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">lockwait</span>(<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">float</span> <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$timeout</span> <span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">=</span> <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">1.0</span>): <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">bool</span></span>

本篇文章学习了 Lock 的值,下篇文章继续学习 Swoole。

请登录后再评论