Consumator producător cu fire și care utilizează boost buffer inel

Am două fire, unul este producătorul și altul este consumator. Consumatorul meu întârzie întotdeauna (din cauza unor apeluri funcționale costisitoare, simulate în codul de mai jos folosind somnuri), așa că am folosit tampon de apel, deoarece îmi permit să pierd câteva evenimente.

Caut să văd dacă blocarea mea arată bine și comentariile generale ale revizuirii c ++.

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

Răspuns

 /* can we do better than this? */ 

În context tampon circular, modalitatea standard de a evita așteptarea ocupată este de a avea două semafore. În primul rând pentru a bloca un producător atunci când un buffer este plin, și în al doilea rând pentru a bloca un consumator atunci când bufferul este gol. Odată ce un proces își trece semaforul și își face treaba, acesta ar trebui să semnaleze peer.


Tamponul circular este bun atunci când consumatorul întârzie doar uneori și nu puteți permiteți pierderea datelor. În situația dvs., pare a fi o soluție greșită: producătorul devine îngustat de rata consumului, iar consumatorul este prezentat cu datele vechi.

Un răspuns tipic este să-l lăsați pe producător să ruleze la viteză maximă. , și triple-tamponează producția (cel puțin, garantează că consumatorul va obține cele mai recente date produse). Vă rog să iertați nerușinata auto-promovare .

Comentarii

  • Vă mulțumim pentru recenzie comentariu și link-ul. Cu toate acestea, în cazul meu, consumatorul meu blochează doar ceva timp. Cred că încă are sens să folosesc semaforele în locul soluției mele actuale folosind condiția variabilă + blocare.
  • @nomanpouigt " ceva timp " este destul de diferit de " întotdeauna târziu ".
  • îmi pare rău, mă refer la tamponul de apel dimensiunea este aleasă astfel încât să poată acomoda întârzierea. Vă întrebați în c ++ nu există semafore și este implementat folosind mută și variabilă de condiție, deci dacă are sens să utilizați semafore.
  • Puteți, de asemenea, să vă elaborați soluția de tampon tripple și cum s-ar aplica în cazul meu ? Vrei să spui că nu ' nu faci nicio blocare în partea producătorului și lasă-l să ruleze și buffer-ul producătorului trebuie să fie tamponat pentru triplu, astfel încât consumatorul să poată obține date recente în caz de depășiri? li>

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *