C ++ kön med fast storlek full / tom signalering

Jag överväger en applikation där stora data måste skrivas till filen ofta. Jag skulle vilja använda en kö och ha en producent och konsument som kör på olika trådar. Dessutom vill jag ha en kö i fast storlek eftersom datastorleken kan vara mycket stor. Jag har implementerat ett enkelt test av boost :: lockfree :: kö med boost :: villkor_variabel för att signalera kön. Jag vill undvika mutex men för undantaget där kön är full (blockproducent) eller tom (blockera konsument)

skulle jag vilja veta (med risk för åsiktsbaserad ..) om jag ” m använder villkor korrekt eller om det finns ett prestandaproblem – jämfört med att använda andra metoder. Här är vad jag hittills har gjort (små 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; } 

Detta fungerar (utdata):

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 

Jag räknar med att data för det mesta nästan alltid kommer att vara tillgängliga men jag vill blockera vid trängsel ( filskrivning, nätverk, etc). Intresset för fast storlek är oro för massiva datauppsättningar och dynamisk fördelning i kön –

(Detta är mer ett experiment i vad som kan göras. I verkligheten uppdateras mina data högst 20 Hz så att bara låsa en std :: kö som jag klarar storleken på kommer också att fungera mycket bra.)

Svar

Använd en semafor för att få producenterna att sova när kön är full och en annan semafor för att få konsumenterna att sova när kön är tom. när kön varken är full eller tom är blockering av sem_post och sem_wait (i nyare kärnor)

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

Svar

Jag skulle vara mycket skeptisk till koden som innehåller en låsfri behållare, två mutexer och en villkorlig variabel för att implementera blockeringskön för tråd. Utan att ens titta längre.

Jag skulle förmodligen börja från prototypen nedan (kanske först kontrollera om boost :: interprocess har något som jag kan använda direkt):

  1. wrap boost::circular_buffer till facebook/Folly/Synchronized men med anpassat skåp som gör try_lock() och sedan snurrar 41 gånger till med try_lock(), blockerar sedan på lock(), räknar förekomster av alla tre scenarierna och med notify / wait på toppen
  2. Jag skulle släppa det till produktion i pilotläge och kontrollera om jag verkligen behöver bry mig om en låsfri behållare.

Kommentarer

  • Jag ’ jag följer inte din pseudokod så bra men jag förstår de problem du har. Jag hade räknat med att användning av scoped_lock skulle göra detta ok, och att alla kräver det ’ s mutex för den villkorliga väntan.

Svar

Boost ger en enda producent-kö för en konsument som är låsfri tror jag, vet du om det? Jag tycker att det passar exakt ditt användningsfall.

http://www.boost.org/doc/libs/1_61_0/doc/html/boost/lockfree/spsc_queue.html

Du kan använda fast storlek och du kommer att ha blockerat konsumenten om data inte är tillgängliga etc. utan att implementera det själv.

Kommentarer

  • Jag gjorde märker detta men min (inte nätverksanslutna) maskin tycktes sakna detta. Med det sagt tror jag att pop och push fungerar på samma sätt, inte blockerande återvändande bool oavsett om det är framgångsrikt eller inte.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *