一般建议
正如一些评论中提到的,我首先看看您是否可以重构您的程序设计,以使互斥体实现对您的性能不再那么重要。
此外,由于标准 C++ 中的多线程支持相当新,而且有些幼稚,因此有时您只需要依靠特定于平台的机制,例如Afutex
Linux 系统上的关键部分或 Windows 上的关键部分或 Qt 等非标准库。
话虽这么说,我可以想到两种可能加速你的程序的实现方法:
Spinlock
如果访问冲突很少发生,并且互斥锁仅保留很短的时间(当然,无论如何我们都应该努力实现这两件事),那么仅使用自旋锁可能是最有效的,因为它不需要任何系统完全调用并且实现起来很简单(取自参考参数 http://en.cppreference.com/w/cpp/atomic/atomic_flag):
class SpinLock {
std::atomic_flag locked ;
public:
void lock() {
while (locked.test_and_set(std::memory_order_acquire)) {
std::this_thread::yield(); //<- this is not in the source but might improve performance.
}
}
void unlock() {
locked.clear(std::memory_order_release);
}
};
当然,缺点是等待线程不会保持睡眠状态并窃取处理时间。
检查锁定
这本质上就是您演示的想法:您首先进行快速检查,基于原子交换操作是否确实需要锁定,并使用重型std::mutex
仅当它不可避免时。
struct FastMux {
//Status of the fast mutex
std::atomic<bool> locked;
//helper mutex and vc on which threads can wait in case of collision
std::mutex mux;
std::condition_variable cv;
//the maximum number of threads that might be waiting on the cv (conservative estimation)
std::atomic<int> cntr;
FastMux():locked(false), cntr(0){}
void lock() {
if (locked.exchange(true)) {
cntr++;
{
std::unique_lock<std::mutex> ul(mux);
cv.wait(ul, [&]{return !locked.exchange(true); });
}
cntr--;
}
}
void unlock() {
locked = false;
if (cntr > 0){
std::lock_guard<std::mutex> ul(mux);
cv.notify_one();
}
}
};
请注意,std::mutex
没有被锁定在中间lock()
and unlock()
但它仅用于处理条件变量。如果互斥锁上存在严重拥塞,这会导致更多的锁定/解锁调用。
您的实施的问题是cv.notify_one();
可以在之间调用if(lockCounter.fetch_add(1, std::memory_order_acquire)>0)
and cv.wait(lock);
所以你的线程可能永远不会醒来。
不过,我没有与您建议的实现的固定版本进行任何性能比较,因此您只需看看什么最适合您。