Spotřebitel výrobce s vlákny a pomocí vyrovnávací paměti boost ring

Mám dvě vlákna, jeden je producent a druhý spotřebitel. Můj zákazník je vždy pozdě (kvůli nějakému nákladnému volání funkce, simulovanému v níže uvedeném kódu pomocí spánků), takže jsem použil prstenovou vyrovnávací paměť, protože si mohu dovolit ztratit některé události.

Dívám se, jestli moje zamykání vypadá dobře a obecné komentáře k recenzi v 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; } 

Odpověď

 /* can we do better than this? */ 

V kontextu kruhové vyrovnávací paměti je standardním způsobem, jak se vyhnout rušnému čekání, mít dva semafory. Nejprve zablokujete producenta, když je vyrovnávací paměť plná, a druhou, abyste zablokovali spotřebitele, když je vyrovnávací paměť prázdná. Jakmile proces projde svým semaforem a provede svou práci, měl by signalizovat partnera.


Kruhová vyrovnávací paměť je dobrá, když se spotřebitel zpozdí jen někdy a nemůžete dovolit ztrátu dat. Ve vaší situaci to vypadá jako špatné řešení: výrobce se zastaví v rychlosti spotřeby a spotřebiteli se zobrazí zastaralá data.

Typickou odpovědí je nechat výrobce běžet plnou rychlostí a ztrojnásobit produkci (alespoň zaručuje, že by spotřebitel získal nejnovější data). Odpusťte prosím nestoudné vlastní propagaci .

Komentáře

  • děkujeme za kontrolu komentář a odkaz. V mém případě však můj spotřebitel blokuje pouze nějaký čas. Myslím, že stále má smysl používat semafory místo mého současného řešení pomocí podmínky variable + lock.
  • @nomanpouigt " nějaký čas " se zcela liší od " vždy pozdě ".
  • omlouvám se, mám na mysli prstenovou vyrovnávací paměť velikost je zvolena tak, aby byla schopna vyrovnat zpoždění. Zajímá vás, že v jazyce C ++ neexistují žádné semafory a je implementován pomocí mutexů a proměnné podmínky, takže pokud má smysl používat semafory.
  • Můžete také vypracovat své řešení tripple bufferu a jak by to platilo v mém případě ? Myslíte tím, že nedělejte ' žádné zamykání na straně výrobce a nechte jej běžet a vyrovnávací paměť výrobce musí být trojnásobně vyrovnávána, aby spotřebitel mohl získat aktuální data v případě překročení?

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *