boost::unique_lock,多次读取会减慢写入速度

boost::unique_lock, multiple reads are slowing down the writer

本文关键字:速度 读取 unique lock boost      更新时间:2023-10-16

我有一个应用程序,我使用boost::shared_mutex锁定在一个线程中写入cv::Mat,然后从许多其他线程调用它。

这本书写得很好,但阅读量太多会让作者慢下来。我想,这是因为写作是在等待所有的阅读完成。有没有一种不同的boost锁可以让编写器全速运行,有很多读者?还是我需要找到另一种方法来解决这个问题?

我的相关代码是:

//writer.h

cv::Mat currentFrame;
typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock > WriteLock;
Lock frameLock;

//writer.cpp

WriteLock w_lock(frameLock);
cv_img.copyTo(currentFrame);
frameLock.unlock();

//阅读器.h

typedef boost::shared_mutex Lock;
typedef boost::shared_lock< Lock > ReadLock;

//reader.cpp

cv::Mat frame;
ReadLock r_lockz(writer->frameLock);
writer->currentFrame.copyTo(frame);
_lockz.unlock();

谢谢。

此处互斥的全部目的是防止读/写冲突。当一个较慢的进程锁定资源时,速度会减慢。

您当前的方法始终存在瓶颈;你需要找到另一个。也许是不同类型的并发(多个读写器?)。

您可以尝试双重缓冲,即使用两个cv:Mat(帧)实例并在它们之间交替。

在从"活动"帧读取(可以是并行的)的同时(按顺序)写入"非活动"帧。写入新帧后,将其指定为"活动",将另一帧指定为"非活动"。

共享状态:

cv::Mat currentFrame[2];
std::shared_mutex frameMutex[2];
std::mutex writeMutex;
std::atomic<int> activeFrame = 0;

编写器实现:

std::unique_lock<std::mutex> writeLock(writeMutex);
const int writeFrame = activeFrame ^ 1;
std::unique_lock<std::shared_mutex> frameLock(frameMutex[writeFrame]);
cv_img.copyTo(currentFrame[writeFrame]);
activeFrame = writeFrame;

阅读器实现:

const int readFrame = activeFrame;
std::shared_lock<std::shared_mutex> frameLock(frameMutex[readFrame]); // lock shared
currentFrame[readFrame].copyTo(frame);

人们的假设是阅读比写作快得多。否则,仍将存在一些锁争用。

对代码进行一点重构,并将"Writer"类中的currentFrame(和相关锁)提取到一个单独的共享"State"类中,这可能是一个好主意。

与其复制,不如分配它并创建一个新的cv::Mat,本质上是一个shared_ptr

//writer.h    
cv::Mat currentFrame;
typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock > WriteLock;
Lock frameLock;
//writer.cpp
WriteLock w_lock(frameLock);
currentFrame = cv_img;
frameLock.unlock();
//reader.h
typedef boost::shared_mutex Lock;
typedef boost::shared_lock< Lock > ReadLock;
//reader.cpp
cv::Mat frame;
ReadLock r_lockz(writer->frameLock);
frame = writer->currentFrame;
_lockz.unlock();

cv_img只是当前使用的缓冲区的前端。读卡器将获得一个在读取周期中期不会改变的缓冲区(但可能在完成之前与当前帧不同步),但其帧的范围决定了每个缓冲区的寿命。

通过进行深度复制,您在shared_ptr(cv::Mat)中创建了一个缓冲区,然后对它的每个用户进行完整复制,而不是创建一个不可变的缓冲区,当每个人都完成时,这个缓冲区就会超出范围。锁定将保持修改。顺便说一句,你可能想考虑在关键部分使用scoped_lock,并在其周围放置大括号或scope。这样,如果你扔了或忘记解锁它,就不会破坏一切。

{
boost::scoped_lock(frameLock);
currentFrame = cv_img;
}

如果您有很多争用,并且是锁定时间(没有争用的锁并不是"那么糟糕"),那么无锁队列可能会工作

#include <boost/lockfree/spsc_queue.hpp>
// writer.h
using cv_queue_t = boost::lockfree::spsc_queue<cv::Mat>// cv::Mat might need this to be wrapped for contructor requirements
cv_queue_t cv_queue;
// writer.cpp
while( !cv_queue.push( cv_img ) ) { // could put a sleep in here }
// reader.cpp
while( !writer->cv_queue.pop( frame ) ) { // could put sleep in here }