webServer设计(一)—— 线程同步类
1 不可复制的类
互斥锁pthread_mutex_t本就不建议通过复制的方式来初始化,而应该在所属线程使用专门的初始化函数进行管理,封装成类之后也应该保证其不被拷贝。
#ifndef BASE_UNCOPYABLE_H #define BASE_UNCOPYABLE_H class uncopyable{ public: //c++11的新机制,确保拷贝构造函数和拷贝复制运算符无效。 uncopyable(const uncopyable&)=delete; void operator=(const uncopyable&)=delete; protected: uncpoyable() {} ~Uncopyable() {} } #endif //BASE_UNCOPYABLE_H
2 互斥锁
使用RAII机制来管理互斥锁mutex,通过作用域来管理临界区,实现锁的自动释放。
//Mutex.h #ifndef BASE_MUTEX_H #define BASE_MUTEX_H #include <pthread.h> #include <assert.h> #include <uncopyable.h> //互斥锁,无法被复制。 class MutexLock : uncopyable { public: MutexLock() { pthread_mutex_init(&mutex_,NULL); } ~MutexLock() { //pthread_mutex_lock(&mutex_); pthread_mutex_destroy(&mutex_); } void lock() { pthread_mutex_lock(&mutex_); } void unlock() { pthread_mutex_unlock(&mutex_); } pthread_mutex_t *getMutex(){ //仅供Condition使用。 return &mutex_; } private: pthread_mutex_t mutex_; private: friend class Condition; }; //临界锁,使用后的区域即为临界区。 class MutexLockGuard : uncopyable { public: explicit MutexLockGuard(MutexLock& mutex):mutex_(mutex) { mutex_.lock(); } //临界区加锁 ~MutexLockGuard(){ mutex_.unlock(); } //临界区解锁 private: MutexLock &mutex_; }; #endif //BASE_MUTEX_H
3 条件量
条件量主要用于线程间的同步,一个线程阻塞于wait函数,等待另外一个线程执行并notify()/notifyAll()。
- 用于主线程等待单个子线程完成任务
- 用于多个子线程等待主线程的“起跑”命令
//Condition.h #ifndef BASE_CONDITION_H #define BASE_CONDITION_H #include <pthread.h> #include <time.h> #include <cstdint> #include "Mutex.h" #include "uncopyable.h" class Condition : uncopyable { public: explicit Condition(MutexLock &mutex):mutex_(mutex) { pthread_cond_init(&cond_,NULL); } ~Condition() { pthread_cond_destroy(&cond_); } void wait() { pthread_cond_wait(&cond_,mutex_.getMutex()); } void notify() { pthread_cond_signal(&cond_); } void notifyAll() { pthread_cond_broadcast(&cond_); } bool waitForSeconds(int seconds) { struct timespec abstime; clock_gettime(CLOCK_REALTIME, &abstime); abstime.tv_sec += static_cast<time_t>(seconds); return ETIMEDOUT == pthread_cond_timedwait(&cond, mutex.get(), &abstime); } private: MutexLock &mutex_; pthread_cond_t cond_; }; #endif //BASE_CONDTION_H
4 倒计时同步
CountDownLatch与Condition的区别主要在于CountDownLatch有一个计数功能,调用cout次countDown之后wait才会停止阻塞。
- 用于主线程等待多个子线程完成初始化;
- 用于多个子线程等待主线程发出“起跑”命令。
//CountDownLatch.h #ifndef BASE_COUNTDOWNLATCH_H #define BASE_COUNTDOWNLATCH_H #include "Condition.h" #include "MutexLock.h" #include "uncopyable.h" class CountDownLatch : uncopyable { public: explicit CountDownLatch(int count); void wait(); void countDown(); private: mutable MutexLock mutex_; Condition condition_; int count_; }; #endif
//CountDownLatch.cpp #include "CountDownLatch.h" CountDownLatch::countDownLatch(int count) :mutex_(),condition(mutex_),count_(count) {} void CountDownLatch::wait() { MutexLockGuard lock(mutex_); while(count_>0) condition_.wait(); } void CountDownLatch::countDown() { MutexLockGuard lock(mutex_); --count_; if(count_==0) condition_.notifyAll(); }
5 总结
- 互斥锁的主要作用是保证对临界区访问的顺序执行。比如进程间共享的条件量或者计数值。
- 条件量主要是用于线程间的同步,一个(或多个)线程阻塞直到另一线程达成条件。
- 倒计时类与条件量相比多了一个计数功能,能够限定代码的执行次数或者保证多个线程的同步。
- 在webserver的设计中,互斥量主要与条件量结合使用,一个是在线程池中,主线程通过条件量等待子线程函数的执行。