Swoole 源码分析2 - 线程锁的封装
Swoole 中对于线程锁的封装位于 'src/lock' 目录,很明了地实现了互斥锁、读写锁、自旋锁三种,结构非常简单。
整体结构
从使用上看, Swoole 将锁操作统一为有限几种(不管锁的原始语义支持不支持),并写为抽象基类中的纯虚函数,实现类似接口的效果。具体的子类继承并实现对应加锁、释放锁的系列操作。
抽象基类
抽象基类 Lock
全贴进来也没几行,除了上面提到的还记录了锁类型和是否进程间共享等信息。
class Lock {
public:
enum Type {
NONE,
RW_LOCK = 1,
FILE_LOCK = 2,
MUTEX = 3,
SEM = 4,
SPIN_LOCK = 5,
ATOMIC_LOCK = 6,
};
Type get_type() {
return type_;
}
virtual ~Lock(){};
virtual int lock_rd() = 0;
virtual int lock() = 0;
virtual int unlock() = 0;
virtual int trylock_rd() = 0;
virtual int trylock() = 0;
protected:
Lock() {
type_ = NONE;
shared_ = false;
}
enum Type type_;
bool shared_;
};
实现类的特殊成员
从设计上讲,子类继承并实现纯虚函数。但不同子类具体实现细节是有区别的,可能需要借助一些自定义数据、操作。而 Swoole 的处理方式是子类增加一个 XxxImpl 类型的成员指针 XxxImpl *impl
。在 Swoole 中,很多模块都有可见类似的操作。以 Mutex 为例:
至于子类 Mutex 中增加的 MutexImpl *impl
成员,很显然就是子类所需的特定数据或方法,当然这里 Mutex 只需要互斥锁及其属性数据:
struct MutexImpl {
pthread_mutex_t lock_;
pthread_mutexattr_t attr_;
};
几种具体实现
三者在初始化时,都会根据是否共享设定相应的 pthread_***attr_t
,其保存于子类中增加的 MutexImpl *impl
成员。这个成员只是一个指针,初始化时会动态申请内存并在子类析构时释放。相关内容可见于: Swoole 源码分析3 - 动态内存管理 ,现在可以先暂时不管这方面细节。
互斥锁
对互斥锁而言是没有所谓获取读锁这种操作的, Swoole 的处理方式是 lock_rd()
和 trylock_rd()
其实都是获取互斥锁,分别对应 lock()
和 trylock()
(相当于这两个方法的别名),最后对应调用 pthread_mutex_***()
系列函数。唯一特别的是 Mutex 单独有个针对 pthread_mutex_timedlock()
的封装,其他两个都没有封装对应的操作。
#ifdef HAVE_MUTEX_TIMEDLOCK
int Mutex::lock_wait(int timeout_msec) {
struct timespec timeo = swoole_time_until(timeout_msec);
return pthread_mutex_timedlock(&impl->lock_, &timeo);
}
#else
int Mutex::lock_wait(int timeout_msec) {
int sub = 1;
int sleep_ms = 1000;
if (timeout_msec > 100) {
sub = 10;
sleep_ms = 10000;
}
while (timeout_msec > 0) {
if (pthread_mutex_trylock(&impl->lock_) == 0) {
return 0;
} else {
usleep(sleep_ms);
timeout_msec -= sub;
}
}
return ETIMEDOUT;
}
#endif
可见在系统不直接支持的情形下, lock_wait()
退而求其次的实现是循环调用 pthread_mutex_trylock()
和 usleep()
,直到设定的超时时间或者获取到锁。
读写锁
对应于抽象基类,读写锁的语义很完整,获取写锁对应于抽象基类中的 lock()
获取锁,其他如获取读锁等则与 pthread_rwlock_***lock()
系列方法一一对应。当然,按照设计,子类 RWLock 中增加的 XxxImpl 成员即为其所需特殊数据或方法:
struct RWLockImpl {
pthread_rwlock_t _lock;
pthread_rwlockattr_t attr;
};
自旋锁
与互斥锁一样,自旋锁没有获取读锁的语义, lock_rd()
和 trylock_rd()
也相当于别名,其他的完全类似。因为 linux 对于 spinlock 没有所谓的属性值,所以子类 SpinLock
的 impl 成员最简单:
class SpinLock : public Lock {
pthread_spinlock_t *impl;
// ......
};
总结
只有非常少量的代码,做了简单的封装。从设计上看并没有设计为所谓的 RAII 模式。目前源码还没有看完,但看到在某些 RAII 作用很大的地方,直接使用的标准库的锁,如 std::unique_lock
。
- ⇦ Swoole 源码分析1 - 协程的上下文切换
- 没有了 ⇨
分类: 编程
标签: Swoole 源码分析