Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[C++/Alg] Library met meerdere threads: callbacks of pollen?

Pagina: 1
Acties:
  • 159 views sinds 30-01-2008
  • Reageer

  • user109731
  • Registratie: Maart 2004
  • Niet online
Ik ben bezig met een library, en het is de bedoeling dat deze een eenvoudige interface krijgt. De library bevat een functie om opdrachten aan te leveren. Die opdrachten worden vervolgens in een std::queue geplaatst, en de functie 'returnt'. Een tweede thread checkt om de zoveel tijd die queue, en verwerkt de opdrachten (als die er zijn). Dit is niet zo'n probleem: de applicatie zal toch het grootste deel van de tijd bezig zijn met opdrachten verwerken.

Nu het probleem: stel dat een opdracht verwerkt is, hoe koppel ik de resultaten hiervan terug naar de applicatie? Ik kan volgens mij het volgende doen:
  • Callbacks: dit vind ik opzich een nette oplossing, en is in C++ eenvoudig te doen met functie pointers. Maar omdat ik twee threads gebruik word de callback op de verwerk-thread uitgevoerd. Ik ben bang dat ik hiermee het probleem teveel opschuif naar de externe applicatie (die moet zich dan gaan bezighouden met locking, threadsafety, etc.). Dat doe ik liever eenmalig zelf :)
  • [br]
  • Pollen: ik kan de resultaten ook in een queue zetten, en de library een functie geven die alle resultaten teruggeeft als die er zijn. De applicatie kan deze functie dan zo nu en dan aanroepen. Het nadeel hiervan is dat de applicatie telkens moet pollen (niet efficient, en niet heel handig voor de ontwikkelaar van de externe applicatie), en dat er mogelijk net nadat er gepollt is opeens wat beschikbaar komt, waardoor de applicatie dit dus later weet dan nodig (geen groot probleem, maar ik vind het gewoon niet zo netjes).
Ik maak gebruik van de boost::thread library. Wat zouden jullie doen in zo'n geval? Of is er een veel beter design mogelijk? Er is genoeg te vinden over callbacks, maar ik vind het lastig om die te gebruiken met verschillende threads :)

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 20:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

Kun je er niet gewoon een event object aan hangen? Ik ken boost::thread niet goed, maar ik neem aan dat ze vergelijkbare functionaliteit hebben als een windows Event handle, waarop een thread kan wachten1. Is die event gesignaled door de worker thread die de taak afhandelt, dan is de data klaar. De applicatie kan dan zelf besluiten om te pollen (kijken of de event gesignaled is), of erop te wachten (door met de thread synchronisatiefunctionaliteit naar de event te luisten). En hij kan ook nog eens beslissen of hij dat allemaal in 1 thread wil doen, of in verschillende. En hij kan ook op meerdere resultaten tegelijk wachten als hij dat wilt, zodat hij een resultaat kan verwerken zodra er een klaar is.

.edit: 1 Dat kan dus met boost::condition :)

[ Voor 14% gewijzigd door .oisyn op 06-09-2007 13:13 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • joepP
  • Registratie: Juni 1999
  • Niet online
JanDM schreef op donderdag 06 september 2007 @ 12:39:
Een tweede thread checkt om de zoveel tijd die queue, en verwerkt de opdrachten (als die er zijn). Dit is niet zo'n probleem: de applicatie zal toch het grootste deel van de tijd bezig zijn met opdrachten verwerken.
Zoals je het hier omschrijft lijkt het dat de workerthread pollt. Het lijkt me beter om hiervoor een ook een Event (of een boost::condition) te gebruiken.

Voor je originele probleem sluit ik me aan bij .oisyn: gebruik een Event-achtig object.

  • user109731
  • Registratie: Maart 2004
  • Niet online
.oisyn schreef op donderdag 06 september 2007 @ 13:01:
.edit: 1 Dat kan dus met boost::condition :)
Dat ziet er geschikt uit :)
joepP schreef op donderdag 06 september 2007 @ 14:07:
[...]

Zoals je het hier omschrijft lijkt het dat de workerthread pollt. Het lijkt me beter om hiervoor een ook een Event (of een boost::condition) te gebruiken.
Ja dat is wel een goed idee... :)

Bedankt heren, ik kan zo weer verder. Ik zat zelf teveel aan callbacks te denken, maar het is zo idd wel het meest flexibel denk ik.

Verwijderd

zonder er al te diep op in te gaan, moet je jezelf afvragen wie er verantwoordelijk is voor de opdracht?

een van de basis regels dat de gene die de opdracht creeert deze ook afhandelt. zoals je zelf al zegt heeft een callback tot gevolg dat de opdracht gever zich gaat bemoeien met de uitvoering er van en dat is niet goed programmeer werk.

je zult dus sowiezo op een soort polling uitkomen want de opdracht gever mag zich niet bemoeien met de uitvoering ervan. de vraag is dus hoe je de opdracht gever gaat vertellen dat zijn opdracht klaar is of beter gezegd waar de resultaat lijst komt te staan.

ik zelf zou voor een systeem waarbij de indiening van een opdracht een soort opdracht-id terug geeft. de opdracht gever kan dan naar eigen inzicht pollen of een bepaalde opdracht al klaar is of pollen of er uberhaupt een opdracht klaar is. een andere functie geeft daan weer het werkelijke resultaat terug.

zelf lijkt een callback mij niet ideaal om je niet kan weten wanneer een opdracht gever tijd heeft om naar de resultaaten te kijken en als library ontwikkelaar moet je je daar ok niet mee bezig houden. de library dit niks meer en niks minder dan opdrachte ontvange, uitvoeren en de resultaten klaar zetten om afgehaald te worden.

dus tenzij je met een tight loop te maken hebt waar een paar ms al te veel is zou je er aan goed aan doen om die scheiding te maken. daarnaast zou je het ook kunnen opdelen in een soort inner en outer library. de inner library communiceert met de outer library via callbacks en de outer library kan dan weer ge-polled worden. op die manier kun je voor gebruikers van je library die geen tijdsnoods hebben meer modulaire manier van pollen gebruiken en zij die wel tijdsnoods hebben kunnen zelf een eigen outer library maken en gebruik maken van de snellere callbacks.

  • EfBe
  • Registratie: Januari 2000
  • Niet online
Pollen? Zijn dat niet kleine zaadjes die in het voorjaar door de lucht zweven?

Pollen is een actie die vroeger nog wel voor kwam maar alleen omdat de programmeur er niet zo'n zin in had om het juist op te lossen of omdat goede oplossingen niet echt voorhanden waren nog. tegenwoordig weten we wel beter en gebruiken we OF events, OF een semaphore gerelateerde oplossing hiervoor, (of een lock georienteerde oplossing).

Pollen kost nl. erg veel performance. Niet alleen heb je lagere throughput want je verspilt tijd om te wachten, maar als je weinig doorstroom hebt in de queue, verstook je ook weer veel CPU tijd om die queue telkens te checken terwijl je WIST dat er niets inzat.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
Events en semaforen zijn leuk voor theoretici, maar eigenlijk net zo nuttig als assembly. Wat de TS nodig heeft is een producer-consumer queue met blocking reads. In een dergelijke queue kan de consumer thread gewoon een read doen op een lege queue. Die read kost 0% CPU en duurt totdat er een item op de queue wordt gezet.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • EfBe
  • Registratie: Januari 2000
  • Niet online
MSalters schreef op maandag 10 september 2007 @ 22:14:
Events en semaforen zijn leuk voor theoretici, maar eigenlijk net zo nuttig als assembly.
hahaha :D. En dat geloof je ook zelf ?

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
Tuurlijk. Beiden zou je moeten begrijpen, maar in de praktijk zijn beiden te moeilijk en te primitief. Daarom zijn er higher-level languages uitgevonden, die in ruil voor een marginale performancehit de code veel begrijpelijker maken. Assembly is daardoor al heel lang erg specialistisch. Multicore development is nog relatief nieuw, en HLL support is nog niet zo goed, maar de richting is duidelijk: 98% van de programmeurs gaat voor de laatste 2% performance niet zelf met semaforen prutsen.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Verwijderd

@EfBe
met 'pollen' doelen we natuurlijk op de actie om te kijken of er nog iets te doen valt. en ja, dat gebeurt overall in je PC, sterker nog: het is een vereiste om a-synchrone te kunnen werken. ook het checken van een events of semophore gebeurt door te pollen. knappe jongen als jij een multi-treaded event-based programma weet te schrijven zonder gebruik te maken van pollen.

en pollen is geen cpu verspilling als je het goed toepast hoeft het niet meer te zijn dan te checken of het nummer van wachtende items groter is dan 0. deze wordt geupdate zodra items in de wachtrij worden geplaatst. pollen hoeft dus niet meer te zijn dan een enkele call naar een enkele integer variable.

door dit slim toe te passen in een goed gestructuureerde programma flow is dit de een prima manier om elke n miliseconde te checken of items de aandacht vereisen, zo niet dan kan het programma iets anders nuttigs gaan doen. daarnaast zou je de poll interval gemakkelijk variable kunnen maken en kunnen verhogen als er herhaaldelijk niets wordt gevonden.

maar de kern van de waarheid is dat pollen gewoon noodzaak is en dat je er vaak niet om heen kunt. het is een vereiste om a-synchrone te kunnen werken want ook event en semaforen zijn gebasseerd op, jawel, pollen. a-synchrone werken vereist dat je op een bepaald moment moet kijken en wachten.

  • citegrene
  • Registratie: Februari 2006
  • Laatst online: 02-11 03:00
Het wiel bestaat al ;)
http://libsigc.sourceforge.net/
http://libsigcx.sourceforge.net/

[ Voor 61% gewijzigd door citegrene op 11-09-2007 22:41 ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 20:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

Verwijderd schreef op dinsdag 11 september 2007 @ 22:29:
@EfBe
met 'pollen' doelen we natuurlijk op de actie om te kijken of er nog iets te doen valt. en ja, dat gebeurt overall in je PC, sterker nog: het is een vereiste om a-synchrone te kunnen werken. ook het checken van een events of semophore gebeurt door te pollen. knappe jongen als jij een multi-treaded event-based programma weet te schrijven zonder gebruik te maken van pollen.
Misschien moet je jouw definitie van pollen even verduidelijken. Is het wachten op een event waarbij de scheduler je thread laat slapen totdat de event geraised wordt ook pollen volgens jou? Want dát is wat er gebeurt. Dus niet dit:
C++:
1
2
while (!eventRaised)
    Sleep(1);

maar dit:
C++:
1
2
if (!eventRaised)
    PauseThread();


Het eerste noem ik pollen. Het tweede vind ik persoonlijk geen pollen, maar daar zou je nog over kunnen debatteren. Ik hoop dat je zelf ook snapt dat er een groot verschil tussen beide methoden zit.
en pollen is geen cpu verspilling als je het goed toepast hoeft het niet meer te zijn dan te checken of het nummer van wachtende items groter is dan 0. deze wordt geupdate zodra items in de wachtrij worden geplaatst. pollen hoeft dus niet meer te zijn dan een enkele call naar een enkele integer variable.

door dit slim toe te passen in een goed gestructuureerde programma flow is dit de een prima manier om elke n miliseconde te checken of items de aandacht vereisen, zo niet dan kan het programma iets anders nuttigs gaan doen. daarnaast zou je de poll interval gemakkelijk variable kunnen maken en kunnen verhogen als er herhaaldelijk niets wordt gevonden.
Ah ja, dus typisch wel het prutswerk van een "busy" wait met sleep. Daar heeft het OS gewoon functionaliteit voor geïmplementeerd, namelijk waitable objects. Dan gaat je thread automatisch verder als dat mogelijk is, en hoef je niet spartaans zelf de hele tijd te sleepen
maar de kern van de waarheid is dat pollen gewoon noodzaak is en dat je er vaak niet om heen kunt. het is een vereiste om a-synchrone te kunnen werken want ook event en semaforen zijn gebasseerd op, jawel, pollen. a-synchrone werken vereist dat je op een bepaald moment moet kijken en wachten.
Nee dus, events en semaphores worden direct ondersteund door de kernel en de thread scheduler. Daar komt geen polling aan te pas. Gelukkig maar, anders zou het een race-condition-hel zijn (door bijvoorbeeld een mutex meteen weer te pakken nadat je 'm vrijgegeven hebt, terwijl een andere thread erop stond te wachten maar geen kans krijgt omdat hij nog aan het slapen is).
boost::signal dus, beetje nutteloos om nog een library te gebruiken als alle functionaliteit in je huidige set van libraries zit ;). Bovendien blijft het probleem van de topicstarter daar nog altijd mee bestaan - je zadelt de user op met multithreaded geneuzel terwijl hij dat misschein wel helemaal niet wilt (aangezien de signal vanaf een andere thread aangeroepen wordt).

[ Voor 12% gewijzigd door .oisyn op 11-09-2007 22:57 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

met events dacht ik meer in de richting van zoals de TS met zijn eigen 'opdracht' event of zoals GTK dat intern doet bijvoorbeeld. user-space events dus. die wil je niet in de kernel hebben lijkt me. stel je voor dat de kernel de events van de GTK library gaat afhandelen.

daarnaast dacht ik meer zoals de TS en dan lijkt het me dat de 'opdracht' tread wel wat anders te doen heeft als de opdracht nog niet af is en dus niet domweg gaat slapen. meer zoals dit:
C++:
1
2
3
4
5
6
 while(!quit_program){
  if(work_done() > 0){
   //doe iets met de opdrachten die klaar zijn
  }
 //doe iets anders nuttigs
 }


ook is de manier van slapen afhankelijk van de mogelijkheden die de taal je biedt en met welk OS je werkt. ook een functie als pausetread() of waitable objects zitten ergens te pollen om te kijken of ze verder mogen. alleen doet de OS scheduler dit en aangezien die het in een grotere scope bekijkt met alle treads en alle proccessen meewegend is dit inderdaad beter voor de performance maar behoort lang niet altijd tot de mogelijkheden en is vaak OS-specefiek. libraries als boost lossen dat dan weer op.

[ Voor 6% gewijzigd door Verwijderd op 11-09-2007 23:18 ]


  • joepP
  • Registratie: Juni 1999
  • Niet online
MSalters schreef op maandag 10 september 2007 @ 22:14:
Events en semaforen zijn leuk voor theoretici, maar eigenlijk net zo nuttig als assembly. Wat de TS nodig heeft is een producer-consumer queue met blocking reads. In een dergelijke queue kan de consumer thread gewoon een read doen op een lege queue. Die read kost 0% CPU en duurt totdat er een item op de queue wordt gezet.
:X

Events zijn juist uitermate nuttig in bovengenoemde situaties, omdat je de consumer de keuze laat hoe hij je library gebruikt. Als ie blocking wil kan dat, maar mocht ie dat niet willen dan kan dat ook. Als je een soort 'jobfactory' maakt zoals de TS mag je echt wel van je gebruikers verwachten dat ze weten hoe een event werkt.

  • EfBe
  • Registratie: Januari 2000
  • Niet online
MSalters schreef op dinsdag 11 september 2007 @ 22:14:
Tuurlijk. Beiden zou je moeten begrijpen, maar in de praktijk zijn beiden te moeilijk en te primitief. Daarom zijn er higher-level languages uitgevonden, die in ruil voor een marginale performancehit de code veel begrijpelijker maken. Assembly is daardoor al heel lang erg specialistisch. Multicore development is nog relatief nieuw, en HLL support is nog niet zo goed, maar de richting is duidelijk: 98% van de programmeurs gaat voor de laatste 2% performance niet zelf met semaforen prutsen.
Maar... als je shared resources hebt in je applicatie en meer dan 1 thread zul je toch wel moeten. (iig een vorm van locking). Events zijn niets anders dan function pointers, zo moeilijk zijn ze niet maar wel erg handig. Maar goed, dit gaat off topic. ;)

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • citegrene
  • Registratie: Februari 2006
  • Laatst online: 02-11 03:00
boost::signal dus, beetje nutteloos om nog een library te gebruiken als alle functionaliteit in je huidige set van libraries zit ;). Bovendien blijft het probleem van de topicstarter daar nog altijd mee bestaan - je zadelt de user op met multithreaded geneuzel terwijl hij dat misschein wel helemaal niet wilt (aangezien de signal vanaf een andere thread aangeroepen wordt).
Multithreading geneuzel? is toch just een leuke uitdaging ;)

Signals is de way to go. Je kan binnen je working thread een aantal signals aanbieden, andere kunnen dan weer functies van hun zelf connecten met zo'n signal.
De functies die geconnect zijn worden op moment dat de signal wordt genotified allemaal uitgevoerd.

Boost heeft inderdaad signals, de CX variant die ik heb gepost heeft echter op multithreading gebied nog een aantal features bovenop die boost kent of de orginele sigc.

  • MicroWhale
  • Registratie: Februari 2000
  • Laatst online: 18-11 10:45

MicroWhale

The problem is choice

Ik heb ooit in delphi een soortgelijke oplossing geschreven. Hierbij had ik een event wat getriggerd werd om een 'result' real-time terug te koppelen.
Voor de GUI had ik wat anders bedacht. Er werd namelijk soms 100 jobs per seconde verwerkt. De GUI zat compleet los (mbv soap) aan een service en haalde op een instelbaar tijdsinterval gegevens op dmv een call.

Het enige belangrijke is dat je vandaag altijd rijker bent dan gisteren. Als dat niet in centen is, dan wel in ervaring.

Pagina: 1