[项目][WebServer][ThreadPool]详细讲解
-
单例模式的线程安全
- 需要双重判空指针,降低锁冲突的概率,提高性能
- 原因1:
- 当第一次实例化单例时,可能有多个线程同时到来,并且svr指针为空
- 这时他们就会去竞争锁,但只有一个线程会最快拿到锁,并且成功实例化出单例对象
- 但此时如果不加双重判空指针,那些也进了第一层if判断的,仍然会去实例化出对象
- 原因2:
- 为了线程安全,必然要加锁,加锁之后再去判空
- 但每次调用
GetInstance()
都需要去获得锁,释放锁,效率低下 - 此时再加一层外层if判空,这样就会避免后续调用
GetInstance()
时没必要的锁竞争
-
static void *ThreadRoutine(void *args)
为什么要设置为static方法?pthread_create
传递给线程的方法只能是返回值为void*
,参数为void*
的函数- static将函数方法声明为静态方法,此时该方法没有隐含的this指针,就可以在类内把这个方法传递给线程调用了
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
-
while()防止伪唤醒
- 可能条件变量唤醒线程时,有多个线程同时被唤醒,但是只有一个最快的线程
PopTask()
可以拿到任务,此时其他线程就会出错 - while()可以在被唤醒的情况下,再次判断任务队列是否有任务
- 这样可以保证,在某个线程醒来的时候,一定是占有互斥锁的
- 可能条件变量唤醒线程时,有多个线程同时被唤醒,但是只有一个最快的线程
static const int THREAD_POOL_NUM = 10;
// 单例模式
class ThreadPool
{
public:
static ThreadPool *GetInstance(int num = THREAD_POOL_NUM)
{
static pthread_mutex_t sMtx = PTHREAD_MUTEX_INITIALIZER;
if (_tp == nullptr)
{
pthread_mutex_lock(&sMtx);
if (_tp == nullptr) // 双重判断,以防线程安全问题
{
_tp = new ThreadPool(num);
_tp->Init();
}
pthread_mutex_unlock(&sMtx);
}
return _tp;
}
// static使该成员函数没有this指针,因为线程执行的函数只能有一个void*参数
static void *ThreadRoutine(void *args)
{
ThreadPool *tp = (ThreadPool *)args;
while(true)
{
Task task;
tp->Lock();
while(tp->TaskQueueIsEmpty()) // while防止伪唤醒
{
tp->ThreadWait();
}
tp->Pop(task);
tp->Unlock(); // 注意,不要在临界资源区内处理任务哦~
task.ProcessOn();
}
}
bool Init()
{
for (int i = 0; i < _num; i++)
{
pthread_t tid;
if (pthread_create(&tid, nullptr, ThreadRoutine, this) != 0)
{
LOG(FATAL, "Create ThreadPool Error");
return false;
}
}
LOG(INFO, "Create ThreadPool Success");
return true;
}
void Push(const Task& task) // in
{
Lock();
_taskQueue.push(task); // 任务队列为临界资源,操作要加锁
Unlock();
ThreadWakeUp();
}
void Pop(Task& task) // out
{
task = _taskQueue.front();
_taskQueue.pop();
}
void ThreadWait()
{
pthread_cond_wait(&_cond, &_mtx);
}
void ThreadWakeUp()
{
pthread_cond_signal(&_cond);
}
bool TaskQueueIsEmpty()
{
return !_taskQueue.size();
}
void Lock()
{
pthread_mutex_lock(&_mtx);
}
void Unlock()
{
pthread_mutex_unlock(&_mtx);
}
bool IsStop()
{
return _stop;
}
~ThreadPool()
{
pthread_mutex_destroy(&_mtx);
pthread_cond_destroy(&_cond);
}
private:
ThreadPool(int num = THREAD_POOL_NUM)
: _num(num), _stop(false)
{
pthread_mutex_init(&_mtx, nullptr);
pthread_cond_init(&_cond, nullptr);
}
ThreadPool(const ThreadPool &) = delete;
private:
int _num;
bool _stop;
std::queue<Task> _taskQueue;
pthread_mutex_t _mtx;
pthread_cond_t _cond;
static ThreadPool *_tp;
};
ThreadPool* ThreadPool::_tp = nullptr;