【发布时间】:2015-05-25 10:49:37
【问题描述】:
我有一个多线程科学应用程序,其中多个计算线程(每个内核一个)必须将它们的结果存储在一个公共缓冲区中。这需要互斥机制。
工作线程只花费一小部分时间写入缓冲区,因此互斥锁大部分时间都处于解锁状态,并且锁定很有可能立即成功,而无需等待另一个线程解锁。
目前,我已经使用 Qt 的 QMutex 来完成这项任务,并且效果很好:互斥锁的开销可以忽略不计。
但是,我只需要将它移植到 c++11/STL。使用 std::mutex 时,性能下降 66%,线程大部分时间都在锁定互斥锁。
在另一个问题之后,我认为 Qt 使用基于简单原子标志的快速锁定机制,针对互斥锁尚未锁定的情况进行了优化。并在并发锁定发生时回退到系统互斥锁。
我想在 STL 中实现它。有没有一种基于 std::atomic 和 std::mutex 的简单方法?我已经深入研究了 Qt 的代码,但它对我的使用来说似乎过于复杂(我不需要锁超时、pimpl、占用空间小等...)。
编辑:我尝试了自旋锁,但效果不佳,因为:
定期(每隔几秒),另一个线程锁定互斥体并刷新缓冲区。这需要一些时间,因此此时所有工作线程都会被阻塞。自旋锁使调度变得忙碌,导致刷新比使用适当的互斥锁慢 10-100 倍。这是不可接受的
编辑:我已经尝试过了,但它不起作用(锁定所有线程)
class Mutex
{
public:
Mutex() : lockCounter(0) { }
void lock()
{
if(lockCounter.fetch_add(1, std::memory_order_acquire)>0)
{
std::unique_lock<std::mutex> lock(internalMutex);
cv.wait(lock);
}
}
void unlock();
{
if(lockCounter.fetch_sub(1, std::memory_order_release)>1)
{
cv.notify_one();
}
}
private:
std::atomic<int> lockCounter;
std::mutex internalMutex;
std::condition_variable cv;
};
谢谢!
编辑:最终解决方案
MikeMB 的快速互斥体运行良好。
作为最终解决方案,我做了:
- 使用带有 try_lock 的简单自旋锁
- 当线程尝试锁定失败时,它们不会等待,而是填充一个队列(不与其他线程共享)并继续
- 当线程获得锁时,它会使用当前结果更新缓冲区,但也会使用存储在队列中的结果(它处理自己的队列)
- 缓冲区刷新效率更高:阻塞部分仅交换两个指针。
【问题讨论】:
-
嗯.. 最好的锁定是不锁定。请问,'buffer'的结构是什么,每个'result'有多大,完成后对buffer做了什么?嫌疑人XY..?
-
您是否尝试将性能与原始 pthread 互斥锁进行比较?
-
你试过自旋锁吗?
-
@MartinJames :缓冲区是一个浮点数组,并且值被累积(添加)。大小太大,每个线程有一个缓冲区。如果有原子浮动我会很高兴:)
-
@galinette:不幸的是,没有——你只能在条件变量、原子和互斥体之上构建一个(我认为这是一个严重的缺陷)