线程池中使用条件变量和信号量的性能比较

面试的时候经常被问到互斥量,条件变量和信号量之间的问题。比如前几天华为面试就被问到互斥量和信号量的区别,说到互斥量也可以使用一个二值信号量来实现,什么情况是只能使用互斥量而不能使用信号量的。这个问题当时我只回答出一种情况,想了解详情的可自行百度。如面试官所说,信号量可以实现互斥量,大部分情况下也可以实现条件变量。甚至使用信号量的实现远比其他实现更容易理解。然而很多时候使用信号量替换条件变量的可能会降低并发程序的性能。
下面是我自己实现的一个线程池的代码:
github地址
这个线程池内部维护一个同步队列,同步队列实现了阻塞的pop和push接口。当队列为空的时候,在一个_emptyCond条件变量上等待;当队列满的时候,在一个_fullCond条件变量上等待。
核心代码如下:

templateT>
T SyncQueue<T>::pop() {
    bool wakePush = false;
    T t;
    {
        ScopedLocker lock(_qMutex);
        wakePush = (_q.size() == _capacity);
        if (_q.empty()) {
            pthread_cond_wait(&_emptyCond, &_qMutex);
        }
        wakePush |= (_q.size() == _capacity);
        if (!_q.empty()) {
            t = _q.front();
            _q.pop_front();
        }
    }
    if (wakePush) {
        pthread_cond_broadcast(&_fullCond);
    }
    return t;
}


template T>
void SyncQueue<T>::push(T t) {
    {
        ScopedLocker lock(_qMutex);
        while (_q.size() == _capacity) {
            pthread_cond_wait(&_fullCond, &_qMutex);
        }
        _q.push_back(t);
    }
    pthread_cond_signal(&_emptyCond);
}

使用这个线程池跑测试程序时,CPU的平均使用率为93%。


现在改为使用信号量的进行同步的队列,核心代码如下(git项目中的feature/semaphore分支):

templateT>
T SyncQueue<T>::pop() {
    bool wakePush = false;
    T t;
    {
        ScopedLocker lock(_qMutex);
        wakePush = (_q.size() == _capacity);
        if (_q.empty()) {
            pthread_cond_wait(&_emptyCond, &_qMutex);
        }
        wakePush |= (_q.size() == _capacity);
        if (!_q.empty()) {
            t = _q.front();
            _q.pop_front();
        }
    }
    if (wakePush) {
        pthread_cond_broadcast(&_fullCond);
    }
    return t;
}


template T>
void SyncQueue<T>::push(T t) {
    {
        ScopedLocker lock(_qMutex);
        while (_q.size() == _capacity) {
            pthread_cond_wait(&_fullCond, &_qMutex);
        }
        _q.push_back(t);
    }
    pthread_cond_signal(&_emptyCond);
}

运行同样的测试程序,CPU占用率只有83%,比使用条件变量的情况低了将近10%。

注:上述数据是使用top命令观察的结果,精确度有待论证,具体数据以实测为准。

你可能感兴趣的:(Linux)