协程同步原语
mutex.h
这个头文件主要用RAII封装了
- ScopedLock:是一个class template,其拥有一个m_mutex成员变量指向真正的锁,其lock、trylock、unlock都是调用m_mutex对应的函数。下面的Mutex、SpinLock、CoMutex都定义了对应类型的ScopedLock,相当于ScopedLock只是一个外壳。
- 自旋锁(SpinLock):封装了pthread_spinlock_t
- 互斥量(Mutex):封装了pthread_mutex_t
- 读锁(ReadScopedLock)和写锁(WriteScopedLock):拥有一个读写锁(RWMutex)作为成员变量,其lock调用的是读写锁的wlock()和rlock()
- 读写锁(RWMutex):封装了pthread_rwlock_t
CoMutex 协程锁
class CoMutex : Noncopyable {
public:
using Lock = ScopedLock<CoMutex>;
bool tryLock();
void lock();
void unlock();
private:
// 协程所持有的锁
SpinLock m_mutex;
// 保护等待队列的锁
SpinLock m_gaurd;
// 持有锁的协程id
uint64_t m_fiberId = 0;
// 协程等待队列
std::queue<std::shared_ptr<Fiber>> m_waitQueue;
};
- lock:调用m_mutex的tryLock尝试获取锁,获取失败就将当前协程加入到协程锁的等待队列并让出协程
- unlock:从等待队列中取出一个等待协程并重新加入调度
CoCondvar 协程条件变量
co_condvar.h头文件只有一个变量CoCondvar,其封装的接口与std::condition_variable一样:wait、notify、notify_all
class CoCondVar : Noncopyable {
public:
using MutexType = SpinLock;
void notify();
void notifyAll();
void wait();
void wait(CoMutex::Lock& lock);
private:
// 协程等待队列
std::queue<std::shared_ptr<Fiber>> m_waitQueue;
// 保护协程等待队列
MutexType m_mutex;
// 空任务的定时器,让调度器保持调度
std::shared_ptr<Timer> m_timer;
};
- notify:从等待队列中取出一个协程并加入调度
- notify_all:将等待队列中的所有协程取出并加入调度
- wait():将当前协程加入到等待队列,并让出协程
CoCountDownLatch 协程计数器
与muduo的CountDownLatch一样
CoSemaphore 协程信号量
co_semaphore.h有两个类
- Semaphore:RAII封装sem_t。这样可能会阻塞协程从而导致阻塞整个线程
- CoSemaphore:通过CoCondVar和CoMutex来同步信号量避免阻塞整个线程
Channel 消息通道
与go语言的chan(channel)基本相同,acid的Channel是一个带有缓冲空间大小的Channel。
其实与muduo的BlockedQueue十分相似,但是这里使用的锁和条件变量都是为了协程专门设定的。
channel.h采用Impl设计模式,就是Channel拥有一个std::shared_ptr<ChannelImpl>指针指向ChannelImpl,Channel的所有功能都是调用ChannelImpl完成。
ChannelImpl成员变量如下:
bool m_isClose;
// Channel 缓冲区大小
size_t m_capacity;
// 协程锁和协程条件变量配合使用保护消息队列
CoMutex m_mutex;
// 入队条件变量
CoCondVar m_pushCv;
// 出队条件变量
CoCondVar m_popCv;
// 消息队列
std::queue<T> m_queue;
主要成员函数:
- push和pop的主要逻辑和阻塞队列一样,不再赘述
<<
和>>
分别封装了push和pop,使用方法与go语言一致
而Channel对外暴露的功能都是借助于ChannelImpl实现,不再赘述。