Multithreading 使用条件变量在线程之间通信
我正在尝试使用多线程实现一种处理直方图图像的算法 最常见的方法之一是拆分多个线程,在每个线程上创建一个缓存缓冲区,在缓存缓冲区上做直方图,然后锁定一个互斥锁,将本地上的值添加到输出向量,然后解锁缓冲区 这种方法非常有效,但会造成“堵塞”。 我的意思是数据的添加不能同时实现 在大多数情况下,当值的范围很短(例如0-255)时,进行加法所需的时间非常快,可以忽略不计 如果数据范围更大,例如在热图像上,这一次可能会变得更重要。 热图像通常是无符号短矩阵,即使值不使用完整范围(0-65535),算法也必须处理所有范围 为了加快一点处理速度,我想启动一个后台线程来进行添加,而“前台”线程只将数据写入预分配的缓冲区 因此,“前台”线程的工作基本上是:Multithreading 使用条件变量在线程之间通信,multithreading,opencv,c++11,Multithreading,Opencv,C++11,我正在尝试使用多线程实现一种处理直方图图像的算法 最常见的方法之一是拆分多个线程,在每个线程上创建一个缓存缓冲区,在缓存缓冲区上做直方图,然后锁定一个互斥锁,将本地上的值添加到输出向量,然后解锁缓冲区 这种方法非常有效,但会造成“堵塞”。 我的意思是数据的添加不能同时实现 在大多数情况下,当值的范围很短(例如0-255)时,进行加法所需的时间非常快,可以忽略不计 如果数据范围更大,例如在热图像上,这一次可能会变得更重要。 热图像通常是无符号短矩阵,即使值不使用完整范围(0-65535),算法也必
- 从循环缓冲区获取缓冲区
- 处理指定数据集的直方图(例如,从第n行到第m行)
- 等待通知到达,检查可用缓冲区的数量是否低于缓冲区的数量
- 如果条件为真,则从可用的缓冲区中查找要处理的缓冲区
- 使用输出缓冲区进行加法
- 使处理后的缓冲区可重用
while(!it->_is_available )
it++;
经过几个小时的测试,我不知道出了什么问题。
我还想知道这个算法是否真的像我想的那样有效?
线程之间是否有更高效、更少处理的通信方式
提前谢谢。我修好了!:)
我从“玩具”一类中找出了几个问题
在操作符()
的重载中,我复制方法get buffer()
的代码。
我围绕等待条件执行了一个阻塞函数,以避免在线程上按顺序执行。
条件变量所需的unique\u lock
仅存在于block函数的方括号之间,如在已评估等待条件时,互斥锁\u mtx\u前台
处于解锁状态
然后,为了防止在搜索可用缓冲区期间出站,已将while
循环替换为for
循环。
问题在于满足指针增量的条件
在“背景”线程上,我添加了一个循环。
其想法是:
类玩具的最新实现如下:
#ifndef TOY
#define TOY
#include <opencv2/core.hpp>
#include <opencv2/core/utility.hpp>
#include <iostream>
#include <iterator>
#include <thread>
#include <mutex>
#include <atomic>
#include <condition_variable>
namespace toy
{
class toy2 : public cv::ParallelLoopBody
{
private:
struct elem_t
{
int _id;
std::atomic_bool _is_available;
std::atomic_bool _is_inprocess;
inline elem_t():
_id(-1),
_is_available(true),
_is_inprocess(false)
{}
// needed for the memory reservation because atomic_bool copy constructor is deleted.
inline elem_t(const elem_t& obj):
_id(obj._id),
_is_available((bool)obj._is_available),
_is_inprocess((bool)obj._is_inprocess)
{}
// needed for the memory reservation because atomic_bool copy constructor is deleted.
inline elem_t(elem_t&& obj):
_id(obj._id),
_is_available((bool)obj._is_available),
_is_inprocess((bool)obj._is_inprocess)
{}
// help for the initialization using iota.
inline elem_t& operator=(const int& id)
{
this->_id = id;
return (*this);
}
};
mutable std::vector<elem_t> _buffer;
std::vector<elem_t*> _elements;
std::atomic_bool _run;
mutable std::atomic_size_t _nb_available_buffers;
mutable std::mutex _mtx_thread;
mutable std::mutex _mtx_foreground;
mutable std::mutex _mtx_background;
mutable std::condition_variable _cv_ctor_dtor;
mutable std::condition_variable _cv_foreground;
mutable std::condition_variable _cv_background;
void background()
{
std::cout<<"background has been detach"<<std::endl;
while(this->_run)
{
{
std::unique_lock<std::mutex> lck(this->_mtx_background);
this->_cv_background.wait(lck);
}
// Condition for stoping terminate the thread.
if(!this->_run)
break;
while(true)
{
typename std::vector<elem_t>::iterator it = std::find_if(this->_buffer.begin(),this->_buffer.end(),[](const elem_t& v){ return (!v._is_available && !v._is_inprocess);});
if(it == this->_buffer.end())
break;
std::cout<<"the background is making the element : "<<it->_id<<" available."<<std::endl;
it->_is_available = true;
it->_is_inprocess = false;
this->_nb_available_buffers++;
this->_cv_foreground.notify_one();
}
}
}
public:
toy2():
_buffer(),
_elements(),
_run(false),
_nb_available_buffers(0),
_mtx_thread(),
_mtx_foreground(),
_mtx_background(),
_cv_ctor_dtor(),
_cv_foreground(),
_cv_background()
{
const int nb_threads = cv::getNumThreads();
this->_nb_available_buffers = nb_threads;
this->_buffer.reserve(nb_threads);
this->_buffer.resize(this->_buffer.capacity());
this->_elements.reserve(this->_buffer.size());
this->_elements.resize(this->_buffer.size(),nullptr);
std::iota(this->_buffer.begin(),this->_buffer.end(),0);
for(int i=0;i<this->_buffer.size();i++)
this->_elements[i] = std::addressof(this->_buffer[i]);
std::thread th([this]
{
// Notify to the constructor.
this->_cv_ctor_dtor.notify_one();
this->background();
// Notify to the destructor.
this->_cv_ctor_dtor.notify_one();
});
this->_run = true;
std::unique_lock<std::mutex> lck(this->_mtx_thread);
th.detach();
this->_cv_ctor_dtor.wait(lck);
}
~toy2()
{
this->_run = false;
this->_cv_background.notify_one();
std::unique_lock<std::mutex> lck(this->_mtx_thread);
this->_cv_ctor_dtor.wait(lck);
}
void operator()(const cv::Range& range)const
{
{
std::unique_lock<std::mutex> lck(this->_mtx_foreground);
this->_cv_foreground.wait(lck,[&]{return this->_nb_available_buffers>0;});
}
typename std::vector<elem_t>::iterator it = std::find_if(this->_buffer.begin(),this->_buffer.end(),[](const elem_t& v){return (bool)v._is_available;});
// for(it = this->_buffer.begin();it != this->_buffer.end();it++)
// if(it->_is_available)
// break;
it->_is_available = false;
it->_is_inprocess = true;
this->_nb_available_buffers--;
std::cout<<"the foreground is processing the element : "<<it->_id<<" "<<std::this_thread::get_id()<<std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(2));
// std::this_thread::sleep_for(std::chrono::seconds(2));
it->_is_inprocess = false;
this->_cv_background.notify_one();
std::cout<<"end thread"<<std::endl;
}
};
}
#endif
\ifndef玩具
#定义玩具
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
名称空间玩具
{
类toy2:公共cv::ParallelLoopBody
{
私人:
结构元素
{
内部id;
std::原子布尔可用;
std::原子布尔在进程中;
内联元素()
_id(-1),
__是否可用(正确),
_正在处理中(错误)
{}
//由于已删除原子复制构造函数,因此需要内存保留。
内联元件(常量元件和对象):
_id(对象id),
_是否可用((bool)obj.\u是否可用),
_正在处理((bool)obj.\u正在处理)
{}
//由于已删除原子复制构造函数,因此需要内存保留。
内联元素(元素和对象):
_id(对象id),
_是否可用((bool)obj.\u是否可用),
_正在处理((bool)obj.\u正在处理)
{}
//使用iota进行初始化的帮助。
内联元素与运算符=(常量int&id)
{
这个->\u id=id;
返回(*本条);
}
};
可变std::vector\u缓冲区;
std::矢量元素;
标准:原子运行;
可变标准::原子大小\u t\u nb\u可用\u缓冲区;
可变std::互斥线程;
可变std::互斥体_mtx_前景;
可变std::互斥体mtx_background;
可变标准::条件变量变量变量变量;
可变标准::条件变量前景;
可变标准::条件变量背景;
无效背景()
{
标准::cout_运行)
打破
while(true)
{
// Method by spining.
// While the available buffer is not find I am looking for it.
// When I'll find I may have done multiple pass.
while(it->_is_available || it->_is_inprocess)
{
it++;
if(it == this->buf_end)
it = this->buf_begin;
}
while(!it->_is_available )
it++;
#ifndef TOY
#define TOY
#include <opencv2/core.hpp>
#include <opencv2/core/utility.hpp>
#include <iostream>
#include <iterator>
#include <thread>
#include <mutex>
#include <atomic>
#include <condition_variable>
namespace toy
{
class toy2 : public cv::ParallelLoopBody
{
private:
struct elem_t
{
int _id;
std::atomic_bool _is_available;
std::atomic_bool _is_inprocess;
inline elem_t():
_id(-1),
_is_available(true),
_is_inprocess(false)
{}
// needed for the memory reservation because atomic_bool copy constructor is deleted.
inline elem_t(const elem_t& obj):
_id(obj._id),
_is_available((bool)obj._is_available),
_is_inprocess((bool)obj._is_inprocess)
{}
// needed for the memory reservation because atomic_bool copy constructor is deleted.
inline elem_t(elem_t&& obj):
_id(obj._id),
_is_available((bool)obj._is_available),
_is_inprocess((bool)obj._is_inprocess)
{}
// help for the initialization using iota.
inline elem_t& operator=(const int& id)
{
this->_id = id;
return (*this);
}
};
mutable std::vector<elem_t> _buffer;
std::vector<elem_t*> _elements;
std::atomic_bool _run;
mutable std::atomic_size_t _nb_available_buffers;
mutable std::mutex _mtx_thread;
mutable std::mutex _mtx_foreground;
mutable std::mutex _mtx_background;
mutable std::condition_variable _cv_ctor_dtor;
mutable std::condition_variable _cv_foreground;
mutable std::condition_variable _cv_background;
void background()
{
std::cout<<"background has been detach"<<std::endl;
while(this->_run)
{
{
std::unique_lock<std::mutex> lck(this->_mtx_background);
this->_cv_background.wait(lck);
}
// Condition for stoping terminate the thread.
if(!this->_run)
break;
while(true)
{
typename std::vector<elem_t>::iterator it = std::find_if(this->_buffer.begin(),this->_buffer.end(),[](const elem_t& v){ return (!v._is_available && !v._is_inprocess);});
if(it == this->_buffer.end())
break;
std::cout<<"the background is making the element : "<<it->_id<<" available."<<std::endl;
it->_is_available = true;
it->_is_inprocess = false;
this->_nb_available_buffers++;
this->_cv_foreground.notify_one();
}
}
}
public:
toy2():
_buffer(),
_elements(),
_run(false),
_nb_available_buffers(0),
_mtx_thread(),
_mtx_foreground(),
_mtx_background(),
_cv_ctor_dtor(),
_cv_foreground(),
_cv_background()
{
const int nb_threads = cv::getNumThreads();
this->_nb_available_buffers = nb_threads;
this->_buffer.reserve(nb_threads);
this->_buffer.resize(this->_buffer.capacity());
this->_elements.reserve(this->_buffer.size());
this->_elements.resize(this->_buffer.size(),nullptr);
std::iota(this->_buffer.begin(),this->_buffer.end(),0);
for(int i=0;i<this->_buffer.size();i++)
this->_elements[i] = std::addressof(this->_buffer[i]);
std::thread th([this]
{
// Notify to the constructor.
this->_cv_ctor_dtor.notify_one();
this->background();
// Notify to the destructor.
this->_cv_ctor_dtor.notify_one();
});
this->_run = true;
std::unique_lock<std::mutex> lck(this->_mtx_thread);
th.detach();
this->_cv_ctor_dtor.wait(lck);
}
~toy2()
{
this->_run = false;
this->_cv_background.notify_one();
std::unique_lock<std::mutex> lck(this->_mtx_thread);
this->_cv_ctor_dtor.wait(lck);
}
void operator()(const cv::Range& range)const
{
{
std::unique_lock<std::mutex> lck(this->_mtx_foreground);
this->_cv_foreground.wait(lck,[&]{return this->_nb_available_buffers>0;});
}
typename std::vector<elem_t>::iterator it = std::find_if(this->_buffer.begin(),this->_buffer.end(),[](const elem_t& v){return (bool)v._is_available;});
// for(it = this->_buffer.begin();it != this->_buffer.end();it++)
// if(it->_is_available)
// break;
it->_is_available = false;
it->_is_inprocess = true;
this->_nb_available_buffers--;
std::cout<<"the foreground is processing the element : "<<it->_id<<" "<<std::this_thread::get_id()<<std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(2));
// std::this_thread::sleep_for(std::chrono::seconds(2));
it->_is_inprocess = false;
this->_cv_background.notify_one();
std::cout<<"end thread"<<std::endl;
}
};
}
#endif