У меня есть кольцевой буфер, который используется для чтения / записи. Я отслеживаю количество, если записи в кольцевом буфере, и не допускаю перезаписи записей, которые не были прочитаны. Я использую std :: condition_variable wait () и notify_one () для синхронизации читателей и писателей. В основном условие для читателя состоит в том, что количество записей> 0. Условие для читателей состоит в том, что количество записей <емкость. </p>
Кажется, все работает, но есть одна вещь, которую я не понимаю,Когда читатель или писатель вызывает notify_one (), это не вызывает переключение контекста. Я прочитал и понимаю, что это работает таким образом. Тем не менее, в случае, когда записывающее устройство записывает запись для заполнения буфера, записывающее устройство вызывает notify_one () и продолжает записывать другое, и в этом случае его предикат завершается ошибкой в своем wait (). В этом случае я вижу, что другой writer () может проснуться, и его предикат также потерпит неудачу. Затем читатель проснется, и его предикат завершится успешно, и он сможет начать читать.
Что я не понимаю, почему на одном notify_one () несколько потоков разблокированы. Разве wait () с ошибочным предикатом не съедает уведомление? Я не могу найти ничего, что утверждает, что это так.
Я мог бы вызвать notify_all () просто для уверенности, но, похоже, он работает с notify_one ().
Вот код.
#include <iostream>
#include <stdint.h>
#include <boost/circular_buffer.hpp>
#include <condition_variable>
#include <thread>
// ring buffer with protection for overwrites
template <typename T>
class ring_buffer {
public:
ring_buffer(size_t size) {
cb.set_capacity(size);
}
void read(T& entry) {
{
std::unique_lock<std::mutex> lk(cv_mutex);
cv.wait(lk, [this] {
std::cout << "read woke up, test=" << (cb.size() > 0) << std::endl;
return 0 < cb.size();});
auto iter = cb.begin();
entry = *iter;
cb.pop_front();
std::cout << "Read notify_one" << std::endl;
}
cv.notify_one();
}
void write(const T& entry) {
{
std::unique_lock<std::mutex> lk(cv_mutex);
//std::cout << "Write wait" << std::endl;
cv.wait(lk, [this] {
std::cout << "write woke up, test=" << (cb.size() < cb.capacity()) << std::endl;
return cb.size() < cb.capacity();});
cb.push_back(entry);
std::cout << "Write notify_one" << std::endl;
}
cv.notify_one();
}
size_t get_number_entries() {
std::unique_lock<std::mutex> lk(cv_mutex);
return cb.size();
}
private:
boost::circular_buffer<T> cb;
std::condition_variable cv;
std::mutex cv_mutex;
};
void write_loop(ring_buffer<int> *buffer) {
for (int i = 0; i < 100000; ++i) {
buffer->write(i);
}
}
void read_loop(ring_buffer<int> *buffer) {
for (int i = 0; i < 50000; ++i) {
int val;
buffer->read(val);
}
}
int main() {
ring_buffer<int> buffer(1000);
std::thread writer(write_loop, &buffer);
std::thread reader(read_loop, &buffer);
std::thread reader2(read_loop, &buffer);
writer.join();
reader.join();
reader2.join();
return 0;
}
В выводе я вижу следующее, когда пробуждаются несколько потоков, поскольку предикат имеет значение false.
read woke up, test=0
read woke up, test=0
write woke up, test=1