Zvažuji aplikaci, kde je třeba do souboru často zapisovat velká data. Chtěl bych použít fronta a producent a spotřebitel běží na různých vláknech. Navíc bych chtěl mít frontu s pevnou velikostí, protože velikost dat může být velmi velká. Implementoval jsem jednoduchý test boost :: lockfree :: queue s podporou :: condition_variable k signalizaci stavu fronty. Chtěl bych se vyhnout mutexu, ale pro výjimku, kdy je fronta plná (producent bloku) nebo prázdná (blok spotřebitele).
Chtěl bych vědět (s rizikem názoru založeného na ..), jestli “ m používám podmíněně správně nebo pokud je problém s výkonem – ve srovnání s použitím jiných metod. Tady je to, co jsem zatím udělal (malá data)
#include <iostream> #include <boost/thread/thread.hpp> #include <boost/lockfree/queue.hpp> #define N 6 #define QN 3 struct testdata { int a; int b; }; boost::lockfree::queue<testdata, boost::lockfree::fixed_size<true>> que(QN); boost::condition_variable que_has_data, que_has_room; boost::mutex que_mtx_hd, que_mtx_hr; void producer(void) { testdata td; int i = 0; boost::mutex::scoped_lock lock(que_mtx_hr); boost::this_thread::sleep(boost::posix_time::seconds(1)); for (;;) { td.a = i; td.b = i + 1; if (!que.push(td)) { std::cout << "producer waiting" << std::endl; que_has_room.wait(lock); } else { std::cout << "pushed " << td.a << std::endl; i += 1; que.has_data_notify_one(); } if (i > N) break; } } void consumer(void) { testdata td; boost::mutex::scoped_lock lock(que_mtx_hd); for (;;) { if (que.pop(td)) { std::cout << "popped " << td.a << std::endl; if (td.a == N) break; que_has_room.notify_one(); } else { std::cout << "consumer waiting" << std::endl; que_has_data.wait(lock); } } boost::this_thread::sleep(boost::posix_time::seconds(1)); } int main(void) { boost::thread t1(&producer); boost::thread t2(&consumer); t1.join(); t2.join(); return 0; }
Toto funguje (výstup):
consumer waiting pushed 0 pushed 1 pushed 2 producer waiting popped 0 pushed 3 producer waiting popped 1 pushed 4 producer waiting popped 2 pushed 5 producer waiting popped 3 pushed 6 popped 4 popped 5 popped 6
Předpokládám, že z větší části budou data téměř vždy k dispozici, ale v případě přetížení chci blokovat ( zápis do souboru, síť atd.) Zájem o pevnou velikost je starostí s masivními datovými sadami a dynamickou alokací ve frontě –
(Jedná se spíše o experiment v tom, co lze udělat. Ve skutečnosti jsou moje data aktualizována maximálně o 20 Hz takže pouhé uzamčení fronty std ::, kterou spravuji, bude fungovat také velmi dobře.)
Odpovědět
Použijte semafor, který způsobí, že producenti usnou, když je fronta plná, a další semafor, který způsobí, že spotřebitelé usnou, když je fronta prázdná. když fronta není ani plná, ani prázdná, operace sem_post a sem_wait neblokují (v novějších jádrech)
#include <semaphore.h> template<typename lock_free_container> class blocking_lock_free { public: lock_free_queue_semaphore(size_t n) : container(n) { sem_init(&pop_semaphore, 0, 0); sem_init(&push_semaphore, 0, n); } ~lock_free_queue_semaphore() { sem_destroy(&pop_semaphore); sem_destroy(&push_semaphore); } bool push(const lock_free_container::value_type& v) { sem_wait(&push_semaphore); bool ret = container::bounded_push(v); ASSERT(ret); if (ret) sem_post(&pop_semaphore); else sem_post(&push_semaphore); // shouldn"t happen return ret; } bool pop(lock_free_container::value_type& v) { sem_wait(&pop_semaphore); bool ret = container::pop(v); ASSERT(ret); if (ret) sem_post(&push_semaphore); else sem_post(&pop_semaphore); // shouldn"t happen return ret; } private: lock_free_container container; sem_t pop_semaphore; sem_t push_semaphore; };
odpověď
Byl bych velmi skeptický, pokud jde o kód obsahující kontejner bez uzamčení, dva mutexy a podmíněnou proměnnou k implementaci fronty blokování mezi vlákny. Aniž bychom se dívali dále.
Pravděpodobně bych začal od níže uvedeného prototypu (možná nejprve zkontroluji, zda má boost :: interprocess něco, co bych mohl hned použít):
- wrap
boost::circular_buffer
dofacebook/Folly/Synchronized
, ale s vlastní skříňkou, kterátry_lock()
otočí ještě 41kráttry_lock()
, poté blokujelock()
, počítá výskyty všech tří scénářů a nahoře upozorňuje / čeká - Vydal bych to do výroby v pilotním režimu a zkontroloval, jestli se opravdu potřebuji obtěžovat bezzámkovým kontejnerem.
Komentáře
- ‚ příliš nesleduji váš pseudo kód, ale chápu vaše obavy. Předpokládal jsem, že použití scoped_lock by to udělalo dobře a že každý vyžaduje ‚ s mutex pro podmíněné čekání.
Odpověď
Boost poskytuje frontu jednoho producenta pro jednoho spotřebitele, která je bezbranková, myslím, víte o ní? Myslím, že přesně odpovídá vašemu případu použití.
http://www.boost.org/doc/libs/1_61_0/doc/html/boost/lockfree/spsc_queue.html
Můžete použít pevnou velikost a zablokujete spotřebitele, pokud nejsou k dispozici data atd., Aniž byste je sami implementovali.
Komentáře
- udělal jsem všimněte si toho, ale zdálo se, že tomuto (ne v síti) stroji chybí. To znamená, že věřím, že pop a push fungují stejně, neblokují návratovou hodnotu, ať už úspěšnou, nebo ne.