在条件变量中不使用谓词会导致速度变慢
Not using predicate in condition variable causing slowdown
我正在尝试构建一个相机包装器,允许其他线程检索帧,而包装器在单独的线程中处理所有处理。 使用 condition 变量通知其他线程新帧已准备就绪的主要目的是使其他线程不会两次获得相同的帧。在某种程度上,这是生产者-消费者问题的一个案例。
在初始化期间,我初始化一个线程以对捕获和处理执行:
int PiCamera::Init(){
camera_on_ = true;
capture_thread_ = std::thread(&PiCamera::RetrieveFrames, this);
return 0;
}
其中检索帧是:
int PiCamera::RetrieveFrames1(){
while(camera_on_){
camera_.grab();
frame_ready_ = true;
ready_condition_.notify_all(); // Notify on condition variable
}
return 0;
}
现在,当有一个线程试图获取帧时,线程需要调用的只是:
int PiCamera::GetFrame1(cv::Mat &image){
// Lock mutex
std::unique_lock<std::mutex> mutex_lock(ready_mutex_);
ready_condition_.wait(mutex_lock, [this](){return frame_ready_;});
camera_.retrieve(image);
frame_ready_ = false;
// Unlock mutex
mutex_lock.unlock();
return 0;
}
现在,如果两个线程调用函数 GetFrame,则每个线程都只能获取 alernate 帧。但是,我希望任意数量的传入线程能够在最新帧可用时立即获取它。
在这里,这似乎是一个具有多个消费者的生产者-消费者问题,但所有消费者都应该能够获得可用的最新数据,并且不应该两次读取相同的数据。
因此,我进行了以下更改:
int PiCamera::RetrieveFrames2(){
while(camera_on_){
camera_.grab();
// frame_ready_ = true;
ready_condition_.notify_all(); // Notify on condition variable
}
return 0;
}
int PiCamera::GetFrame2(cv::Mat &image){
// Lock mutex
std::unique_lock<std::mutex> mutex_lock(ready_mutex_);
// ready_condition_.wait(mutex_lock, [this](){return frame_ready_;});
ready_condition_.wait(mutex_lock);
camera_.retrieve(image);
// frame_ready_ = false;
// Unlock mutex
mutex_lock.unlock();
return 0;
}
现在我可以使用 2 个线程来获得相同的帧,但我注意到检索帧时有些变慢。
我运行的程序有点像这样:
PiCamera camera();
camera.Init();
cv::Point2f centroid_location;
cv::Mat image;
float time1[NFRAMES] = {};
float time2[NFRAMES] = {};
float time3[NFRAMES] = {};
timeval tstart, tend, t1, t2, t3;
for(int frame=0;frame<NFRAMES;frame++){
gettimeofday(&t1, nullptr);
camera->GetFrame(image);
gettimeofday(&t2, nullptr);
time1[frame] = ElapsedSec(t1, t2)*1000;
GetCentroid(image, centroid_location);
// Just to increase workload
GetCentroid(image, centroid_location);
gettimeofday(&t3, nullptr);
time2[frame] = ElapsedSec(t2, t3)*1000;
}
gettimeofday(&tend, nullptr);
float total_time = ElapsedSec(tstart, tend);
float fps = (float)NFRAMES/total_time;
std::cout << "Camera took " << total_time << " seconds at " << fps << " FPSn";
std::cout << "t1 " << ArrayMean(time1, NFRAMES) << " t2 " << ArrayMean(time2, NFRAMES) << 'n';
相机能够以 120FPS 的帧速率抓取,所以我希望我也能够以 120FPS 的速度处理帧。
当我使用RetrieveFrames1和GetFrames1运行程序时,我得到这个:
Camera took 8.39579 seconds at 119.107 FPS
t1 0.367703 t2 8.02553
但是,当我使用 RetrieveFrames2 和GetFrames2进行此测试时:
Camera took 13.7088 seconds at 72.946 FPS
t1 4.74955 t2 8.95662
即使我只调用一次GetCentroid,我也会得到以下结果:
Camera took 8.29365 seconds at 120.574 FPS
t1 3.98624 t2 4.30591
和
Camera took 8.94322 seconds at 111.817 FPS
t1 4.43984 t2 4.50159
为什么我的线程需要更长的时间等待这里的条件变量,而我所做的唯一事情就是删除谓词?
如果有人遇到这种情况,我最终使用 https://github.com/rigtorp/MPMCQueue 来抽象出在消费者和生产者线程之间传输数据的逻辑
int PiCamera::RetrieveFrames1(){
while(camera_on_){
camera_.grab();
frame_ready_ = true;
ready_condition_.notify_all(); // Notify on condition variable
}
这已损坏,因为此线程修改frame_ready_
而不持有保护它的互斥锁。
int PiCamera::RetrieveFrames2(){
while(camera_on_){
camera_.grab();
// frame_ready_ = true;
ready_condition_.notify_all(); // Notify on condition variable
}
return 0;
这已损坏,因为它调用notify_all
而不更改受互斥锁保护的任何共享状态。您必须在调用notify_all
互斥锁之前或调用notify_all
之后但在释放互斥锁之前保持互斥锁时更改共享状态。
int PiCamera::GetFrame2(cv::Mat &image){
// Lock mutex
std::unique_lock<std::mutex> mutex_lock(ready_mutex_);
// ready_condition_.wait(mutex_lock, [this](){return frame_ready_;});
ready_condition_.wait(mutex_lock);
camera_.retrieve(image);
// frame_ready_ = false;
// Unlock mutex
mutex_lock.unlock();
return 0;
}
这已损坏,因为它调用wait
,而没有首先检查受互斥锁保护的共享状态是否处于它正在等待的状态以外的状态。你不能等待已经发生的事情。在调用wait
之前,您必须检查 shrared 状态。
条件变量在且仅当它们与受互斥锁保护的共享状态结合使用时才能很好地工作。调用通知函数的线程必须在互斥锁的保护下更改共享状态。等待的线程必须检查互斥锁保护下的共享状态,以确定它们是否需要等待,然后确定它们是否完成等待。
虽然可以打破这些规则并仍然让你的代码工作,但需要深厚的知识和专业知识才能可靠地使用条件变量。
为什么我的线程需要更长的时间等待这里的条件变量,而我所做的唯一事情就是删除谓词?
因为没有什么能阻止线程等待已经发生的事情。
- std::condition_variable::wait()如何评估给定的谓词
- 有没有办法将谓词中的元素偏移量传递给 std 算法?
- 为什么在读取文件大小时文件IO速度会发生变化
- 为什么std::condition_variable notify_all的工作速度比notify_one快(对于随机请
- 文件系统:复制功能的速度秘诀是什么
- 学习多线程C++:添加线程不会使执行速度更快,即使它看起来应该
- 在C++中使用并行化的预期速度是多少(不是 OpenMp,而是 <thread>)
- 两个连续的 OpenMP 并行区域会相互减慢速度
- 如何在 STL 函数中找到传递给谓词的元素的索引?
- 查找标准::hash_map与标准::矢量的速度
- 加快在C++中读取/处理日志文件的速度
- 为什么这些算法的运行速度比它们应该的要快?
- 为 C++11 算法组合多个谓词
- 如何提高文件的读取速度?
- 通过libpqxx提高PostgreSQL数据库的更新速度
- 使用 IMFSinkWriter 编码的视频的播放速度会根据宽度而变化
- 计算车辆之间的距离并设置速度,使距离保持不变,例如 5 米
- 如何加快大字符串的解析速度?
- 处理 std::enable_if<...中谓词的逻辑"OR">
- 在条件变量中不使用谓词会导致速度变慢