1,使用
TQueueConcurrent<std::vector<std::string>> fifo_queue;
...
...
fifo_queue.emplace_back(string_ret);
LOG(INFO) << "fifo_queue size: " << fifo_queue.size();
...
...
if(!fifo_queue.is_empty()) {
std::vector<std::string> string_ret = fifo_queue.pop_front_not_wait();
...
...
}
}
2,实现
template< typename T >
class TQueueConcurrent {
using const_iterator = typename std::deque<T>::const_iterator;
public:
//! \brief Emplaces a new instance of T in front of the deque
template<typename... Args>
void emplace_front( Args&&... args )
{
addData_protected( [&] {
_collection.emplace_front(std::forward<Args>(args)...);
} );
}
//! \brief Emplaces a new instance of T at the back of the deque
template<typename... Args>
void emplace_back( Args&&... args )
{
addData_protected( [&] {
_collection.emplace_back(std::forward<Args>(args)...);
} );
}
//! \brief Returns the front element and removes it from the collection
//!
//! No exception is ever returned as we garanty that the deque
//is not empty
//! before trying to return data.
T pop_front( void ) noexcept
{
std::unique_lock<std::mutex> lock{_mutex};
while (_collection.empty()) {
_condNewData.wait(lock);
}
auto elem = std::move(_collection.front());
_collection.pop_front();
return elem;
}
T pop_front_not_wait( void ) noexcept
{
std::unique_lock<std::mutex> lock{_mutex};
auto elem = std::move(_collection.front());
_collection.pop_front();
lock.unlock();
return elem;
}
bool is_empty() {
std::unique_lock<std::mutex> lock{_mutex};
bool is_empty = _collection.empty();
lock.unlock();
return is_empty;
}
int size(){
std::unique_lock<std::mutex> lock{_mutex};
int size = _collection.size();
lock.unlock();
return size;
}
private:
//! \brief Protects the deque, calls the provided function and
//notifies the presence of new data
//! \param The concrete operation to be used. It MUST be an operation
//which will add data to the deque,
//! as it will notify that new data are available!
template<class F>
void addData_protected(F&& fct)
{
std::unique_lock<std::mutex> lock{ _mutex };
fct();
lock.unlock();
_condNewData.notify_one();
}
std::deque<T> _collection; ///< Concrete, not thread safe, storage.
std::mutex _mutex; ///< Mutex protecting the concrete storage
std::condition_variable _condNewData; ///< Condition used to notify
///that new data are available.
};
3,知识点
1, std::mutex
https://blog.csdn.net/u012507022/article/details/85909567
std::mutex是C++11中最基本的互斥量,std::mutex对象提供了独占所有权的特性,不支持递归地对std::mutex对象上锁。
std::mutex成员函数:
(1)、构造函数:std::mutex不支持copy和move操作,最初的std::mutex对象是处于unlocked状态。
(2)、lock函数:互斥锁被锁定。线程申请该互斥锁,如果未能获得该互斥锁,则调用线程将阻塞(block)在该互斥锁上;如果成功获得该互诉锁,该线程一直拥有该互斥锁直到调用unlock解锁;如果该互斥锁已经被当前调用线程锁住,则产生死锁(deadlock)。
(3)、unlock函数:解锁,释放调用线程对该互斥锁的所有权。
(4)、try_lock:尝试锁定互斥锁。如果互斥锁被其他线程占有,则当前调用线程也不会被阻塞,而是由该函数调用返回false;如果该互斥锁已经被当前调用线程锁住,则会产生死锁。其中std::mutex就是lock、unlock。std::lock_guard与std::mutex配合使用,把锁放到lock_guard中时,mutex自动上锁,lock_guard析构时,同时把mutex解锁。
(5)、native_handle:返回当前句柄。
2,std::condition_variable
https://blog.csdn.net/fengbingchun/article/details/73695596
条件变量std::condition_variable用于多线程之间的通信,它可以阻塞一个或同时阻塞多个线程。std::condition_variable需要与std::unique_lock配合使用。std::condition_variable效果上相当于包装了pthread库中的pthread_cond_*()系列的函数。
当std::condition_variable对象的某个wait函数被调用的时候,它使用std::unique_lock(通过std::mutex)来锁住当前线程。当前线程会一直被阻塞,直到另外一个线程在相同的std::condition_variable对象上调用了notification函数来唤醒当前线程。
std::condition_variable对象通常使用std::unique_lock<std::mutex>来等待,如果需要使用另外的lockable类型,可以使用std::condition_variable_any类。
std::condition_variable类的成员函数:
(1)、构造函数:仅支持默认构造函数,拷贝、赋值和移动(move)均是被禁用的。
(2)、wait:当前线程调用wait()后将被阻塞,直到另外某个线程调用notify_*唤醒当前线程;当线程被阻塞时,该函数会自动调用std::mutex的unlock()释放锁,使得其它被阻塞在锁竞争上的线程得以继续执行。一旦当前线程获得通知(notify,通常是另外某个线程调用notify_*唤醒了当前线程),wait()函数也是自动调用std::mutex的lock()。wait分为无条件被阻塞和带条件的被阻塞两种。
无条件被阻塞:调用该函数前,当前线程应该已经对unique_lock<mutex> lck完成了加锁。所有使用同一个条件变量的线程必须在wait函数中使用同一个unique_lock<mutex>。该wait函数内部会自动调用lck.unlock()对互斥锁解锁,使得其他被阻塞在互斥锁上的线程恢复执行。使用本函数被阻塞的当前线程在获得通知(notified,通过别的线程调用 notify_*系列的函数)而被唤醒后,wait()函数恢复执行并自动调用lck.lock()对互斥锁加锁。
带条件的被阻塞:wait函数设置了谓词(Predicate),只有当pred条件为false时调用该wait函数才会阻塞当前线程,并且在收到其它线程的通知后只有当pred为true时才会被解除阻塞。因此,等效于while (!pred()) wait(lck).
(3)、wait_for:与wait()类似,只是wait_for可以指定一个时间段,在当前线程收到通知或者指定的时间超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其它线程的通知,wait_for返回,剩下的步骤和wait类似。
(4)、wait_until:与wait_for类似,只是wait_until可以指定一个时间点,在当前线程收到通知或者指定的时间点超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其它线程的通知,wait_until返回,剩下的处理步骤和wait类似。
(5)、notify_all: 唤醒所有的wait线程,如果当前没有等待线程,则该函数什么也不做。
(6)、notify_one:唤醒某个wait线程,如果当前没有等待线程,则该函数什么也不做;如果同时存在多个等待线程,则唤醒某个线程是不确定的(unspecified)。