Producentforbruger med tråde og bruger boost ringbuffer

Jeg har to tråde, den ene er producenten og den anden er forbruger. Min forbruger er altid sent (på grund af et dyrt funktionsopkald, simuleret i nedenstående kode ved hjælp af sleeps), så jeg har brugt ringbuffer, da jeg har råd til at miste nogle begivenheder.

Jeg ser efter, om min låsning ser ud til at være i orden, og generelle kommentarer til c ++ gennemgår.

#include <iostream> #include <thread> #include <chrono> #include <vector> #include <atomic> #include <boost/circular_buffer.hpp> #include <condition_variable> #include <functional> std::atomic<bool> mRunning; std::mutex m_mutex; std::condition_variable m_condVar; class VecBuf { private: std::vector<int8_t> vec; public: VecBuf() = default; VecBuf(std::vector<int8_t> v) { vec = v; } }; std::vector<int8_t> data{ 10, 20, 30 }; class Detacher { public: template<typename Function, typename ... Args> void createTask(Function &&func, Args&& ... args) { m_threads.emplace_back(std::forward<Function>(func), std::forward<Args>(args)...); } Detacher() = default; Detacher(const Detacher&) = delete; Detacher & operator=(const Detacher&) = delete; Detacher(Detacher&&) = default; Detacher& operator=(Detacher&&) = default; ~Detacher() { for (auto& thread : m_threads) { thread.join(); } } private: std::vector<std::thread> m_threads; }; void foo_1(boost::circular_buffer<VecBuf> *cb) { while (mRunning) { std::unique_lock<std::mutex> mlock(m_mutex); m_condVar.wait(mlock, [=]() { return !cb->empty(); }); VecBuf local_data(cb->front()); cb->pop_front(); mlock.unlock(); if (!mRunning) { break; } //simulate time consuming function call and consume local_data here std::this_thread::sleep_for(std::chrono::milliseconds(16)); } while (cb->size()) { VecBuf local_data(cb->front()); cb->pop_front(); if (!mRunning) { break; } } } void foo_2(boost::circular_buffer<VecBuf> *cb) { while (mRunning) { std::unique_lock<std::mutex> mlock(m_mutex); while (cb->full()) { mlock.unlock(); /* can we do better than this? */ std::this_thread::sleep_for(std::chrono::milliseconds(100)); mlock.lock(); } cb->push_back(VecBuf(data)); m_condVar.notify_one(); } } int main() { mRunning = true; boost::circular_buffer<VecBuf> cb(100); Detacher thread_1; thread_1.createTask(foo_1, &cb); Detacher thread_2; thread_2.createTask(foo_2, &cb); std::this_thread::sleep_for(std::chrono::milliseconds(20000)); mRunning = false; } 

Svar

 /* can we do better than this? */ 

I en cirkulær bufferkontekst er den standard måde at undgå travlt med at vente på at have to semaforer. Først til at blokere en producent, når en buffer er fuld, og for det andet at blokere en forbruger, når bufferen er tom. Når en proces har passeret sin semafor og gør sit job, skal den signalere peer.


Den cirkulære buffer er god, når forbrugeren kun nogle gange er sent og du kan ikke råd til at miste data. I din situation ser det ud som en forkert løsning: producenten bliver nedbrudt af forbruget, og forbrugeren præsenteres med de uaktuelle data.

Et typisk svar er at lade producenten køre i fuld hastighed. , og tredobbelt bufferproduktionen (i det mindste garanterer det, at forbrugeren får de senest producerede data). Tilgiv den skamløse selvreklame .

Kommentarer

  • tak for anmeldelsen kommentar og linket. Men i mit tilfælde blokerer min forbruger kun noget tid. Jeg synes, det stadig giver mening at bruge semaforer i stedet for min nuværende løsning ved hjælp af betingelsesvariabel + lås.
  • @nomanpouigt " et stykke tid " er ret forskellig fra " altid sent ".
  • undskyld, jeg mener ringbufferen størrelse vælges på en sådan måde, at den er i stand til at accomodere forsinkelsen. Spekulerer i c ++ er der ingen semaforer, og den implementeres ved hjælp af mutexes og betingelsesvariabler, så hvis det giver mening at bruge semaforer.
  • Kan du også uddybe din løsning af tredobbelt buffer og hvordan ville det gælde i mit tilfælde ? Mener du ikke ' ikke foretage nogen låsning i producentsiden og lade den køre, og producentbufferen skal tredobles, så forbrugeren kan få nylige data i tilfælde af overskridelser?

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *