Producent consument met threads en gebruik boost ring buffer

Ik heb twee threads, de ene is de producent en de andere is de consument. Mijn klant is altijd te laat (vanwege een dure functieaanroep, gesimuleerd in onderstaande code met behulp van slaapstanden), dus ik heb een ringbuffer gebruikt omdat ik het me kan veroorloven om sommige gebeurtenissen te verliezen.

Ik ben aan het kijken of mijn vergrendeling er goed uitziet en algemene c ++ recensie-opmerkingen.

#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; } 

Antwoord

 /* can we do better than this? */ 

In een circulaire buffercontext is de standaardmanier om druk wachten te vermijden twee semaforen hebben. Ten eerste om een producent te blokkeren wanneer een buffer vol is, en ten tweede om een consument te blokkeren wanneer de buffer leeg is. Zodra een proces zijn semafoor passeert en zijn werk doet, zou het de peer moeten signaleren.


De circulaire buffer is goed wanneer de consument slechts soms te laat is en je kunt niet verlies van gegevens veroorloven. In jouw situatie lijkt het een verkeerde oplossing: de producent wordt afgeremd door de snelheid van consumptie en de consument krijgt de verouderde gegevens te zien.

Een typisch antwoord is om de producent op volle snelheid te laten draaien , en de productie drievoudig bufferen (het garandeert tenminste dat de consument de meest recent geproduceerde gegevens krijgt). Vergeef alsjeblieft de schaamteloze zelfpromotie .

Reacties

  • bedankt voor de recensie commentaar en de link. In mijn geval blokkeert mijn consument echter maar een tijdje. Ik denk dat het nog steeds logisch is om semaforen te gebruiken in plaats van mijn huidige oplossing met condition variable + lock.
  • @nomanpouigt " enige tijd " verschilt nogal van " altijd laat ".
  • sorry, ik bedoel de ringbuffer grootte is zo gekozen dat het de vertraging kan opvangen. Vraagt u zich af in c ++ dat er geen semaforen zijn en het wordt geïmplementeerd met behulp van mutexes en condition variabele, dus of het zinvol is om semaforen te gebruiken.
  • Kunt u alstublieft ook uw oplossing van tripple buffer uitwerken en hoe zou dit in mijn geval van toepassing zijn ? Bedoel je niet ' te vergrendelen aan de producentkant en het te laten draaien en de producentbuffer moet drievoudig gebufferd worden zodat de consument recente gegevens kan krijgen in geval van overschrijding?

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *