Toon posts:

OO-design - threading & locking

Pagina: 1
Acties:

Verwijderd

Topicstarter
Hoi allemaal,

Ik ben bezig met het maken van een kleine library, en kom een aantal threading-problemen tegen. Er communiceren twee threads met mijn library:

1) event thread: deze thread geeft library aan welke events er gebeuren in het systeem
2) user/API thread: de gebruiker roept API functies op uit zijn eigen thread

Laat ik het heel simpel voorstellen:

C++:
1
2
3
4
5
6
7
class Library
{
    shared data
    
    void EventFunction(); /* called by event thread */
    void APIFunction();   /* called by API thread */
}


Beide functies maken dus gebruik van shared data. Mijn vraag is nu: hoe kan ik deze shared data het beste afschermen? In feite kan ik simpel bij elke event- en API-functie een mutex aanvragen, en weer vrijgeven bij het einde van deze functie. Mij lijkt dit geen goede oplossing, wegens de overhead van het locken en vrijgeven van de mutex.

Daarom dacht ik extra thread bovenop de klasse Library te bouwen. Elke event die ik binnenkrijg post ik naar die thread, en elke API-functie-call post ik eveneens naar die thread. Op die manier moet ik mij in de eigenlijke implementatie van Library niet druk maken om threading issues, want hij wordt maar door die ene thread gebruikt:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Library
{
    public void EventFunction(Event); /* called by LibraryThreadSafe*/
    public ReturnWaarde APIFunction();   /* called by LibraryThreadSafe */
}

class LibraryThreadSafe : public Library
{
    WaitQueue queue;

    public ReturnWaarde APIFunction()
    {
        queue.add(call van deze functie);
        wacht op resultaat
        return resultaat
    }

    public void EventFunction(Event e)
    {
        queue.add(e)
    }

    public void run()
    {
        while (queue.get())
            verwerk
    }
}


Het probleem van deze aanpak is wel dat ik het moeilijk ga hebben met returnwaarden en excepties van API-functies. Ik zal waarschijnlijk voor elke API-functie een eigen klasse moeten aanmaken, dewelke de resultaten en excepties bevat, zodat ik deze terug kan gegeven aan de user.

Wat denken jullie? Bedankt!

  • Cavalera125
  • Registratie: December 2003
  • Laatst online: 26-11 07:25
Ik zou gewoon een mutex gebruiken. Met die extra thread genereer je volgens mij meer overhead dan met een mutex...

  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

Verwijderd schreef op woensdag 09 mei 2007 @ 13:02:
Beide functies maken dus gebruik van shared data. Mijn vraag is nu: hoe kan ik deze shared data het beste afschermen? In feite kan ik simpel bij elke event- en API-functie een mutex aanvragen, en weer vrijgeven bij het einde van deze functie. Mij lijkt dit geen goede oplossing, wegens de overhead van het locken en vrijgeven van de mutex.

Daarom dacht ik extra thread bovenop de klasse Library te bouwen. Elke event die ik binnenkrijg post ik naar die thread, en elke API-functie-call post ik eveneens naar die thread. Op die manier moet ik mij in de eigenlijke implementatie van Library niet druk maken om threading issues, want hij wordt maar door die ene thread gebruikt:
Als je dat doet, dan serialiseer je dus alle toegang tot de library. Als je performance vereisten niet zodanig zijn dat meerdere threads tegelijk van de library gebruik moeten kunnen maken, dan is dat natuurlijk een prima oplossing die je een hoop zorgen om concurrency problemen scheelt.

Als je je library met meerdere threads tegelijk wilt kunnen benaderen, dan ontkom je niet aan het gebruik van mutexen (er vanuitgaande dat er anders concurrency issues ontstaan; er zijn best libraries denkbaar die concurrent gebruikt kunnen worden zonder concurrency issues, bijvoorbeeld als een thread die alleen leest niet op de kloktik de laatste informatie hoeft te hebben en er geen risico op 'gedeeltelijke' informatie is ).

Wie trösten wir uns, die Mörder aller Mörder?


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Ik zou eerst eens kijken of de performance met een lock (mutex) voldoende is. Is dit niet voldoende, dan zou je eventueel naar andere oplossingen kunnen kijken zoals licht verouderde informatie terug sturen waarbij je geen lock meer nodig bent om bij deze informatie te komen.

Premature optimization is the root of all evil (and women too)

ps:
als je gaat werken met een queue om opdrachten in op te slaan, zal er in die queue ook gesynchroniseerd moeten worden. Ik ben verder niet bekend met c++ maar je zou eventueel ook kunnen kijken naar een readwritelock. Op die manier zullen readers elkaar in ieder geval niet blokkeren, maar ik betwijfel of een readwritelock nu echt voordeeld biedt als je extreem kort een lock vast houdt (een readwritelock heeft zelf namelijk ook synchronizatie intern nodig om zijn consistentie te kunnen garanderen).

[ Voor 42% gewijzigd door Alarmnummer op 10-05-2007 07:32 ]


  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

Alarmnummer schreef op donderdag 10 mei 2007 @ 07:19:
Ik zou eerst eens kijken of de performance met een lock (mutex) voldoende is. Is dit niet voldoende, dan zou je eventueel naar andere oplossingen kunnen kijken zoals licht verouderde informatie terug sturen waarbij je geen lock meer nodig bent om bij deze informatie te komen.

Premature optimization is the root of all evil
Bedoel je dat je een oplossing met volatile variabelen (als we even java spreken) als een premature optimization beschouwd? Er lijkt mij weinig premature aan het niet aanleggen van synchronized getters en setters op variabelen waarvan het echt niet uitmaakt of ze wat later worden gelezen ( zoals vaak in constructies met "while (!stoppingConditionSatisfied) { }" in een run() ).

Wie trösten wir uns, die Mörder aller Mörder?


  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 07:54

Janoz

Moderator Devschuur®

!litemod

Nee, ik denk dat Alarmnummer eerder bedoelt dat het hele queue idee wat premature is. Het niet goed synchroniseren levert gewoon foute code op, en niet gewoon een andere 'foutloze' implementatie.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Confusion schreef op donderdag 10 mei 2007 @ 08:13:
[...]

Bedoel je dat je een oplossing met volatile variabelen (als we even java spreken) als een premature optimization beschouwd? Er lijkt mij weinig premature aan het niet aanleggen van synchronized getters en setters op variabelen waarvan het echt niet uitmaakt of ze wat later worden gelezen ( zoals vaak in constructies met "while (!stoppingConditionSatisfied) { }" in een run() ).
Ik probeer het achterliggende probleem van de TS aan de kaak te stellen: hij probeert direct naar gecompliceerdere oplossingen te grijpen terwijl hij niet weet of de simpele oplossing voldoende is.

Het gebruik van volatile's is trouwens ook niet volledig zonder problemen (vooral bij gecompliceerdere data structuren en oudere virtual machine's die JSR-133 niet ondersteunen).

voorbeeld:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class SomeEvent{
    String msg;

    SomeEvent(String msg){this.msg = msg}
    String getMsg(){return msg;}
}

class SomeEventContainer{
    volatile SomeEvent lastEvent;

   void setLastEvent(SomeEvent lastEvent){
        this.lastEvent = lastEvent;
   }

   SomeEvent getLastEvent(){
      return lastEvent;
   }
}


De concurrency connaisseurs zullen misschien het probleem al zien.

[ Voor 18% gewijzigd door Alarmnummer op 10-05-2007 09:47 ]


  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

Janoz schreef op donderdag 10 mei 2007 @ 09:34:
Nee, ik denk dat Alarmnummer eerder bedoelt dat het hele queue idee wat premature is. Het niet goed synchroniseren levert gewoon foute code op, en niet gewoon een andere 'foutloze' implementatie.
De toegang tot de library serialiseren door alle requests op een queue te plaatsen is denk ik een makkelijkere manier op de library threadsafe te maken dan de hele library van fine grained locking voorzien.
Een Collection.synchronizedList wrapper is ook een stuk makkelijker te bouwen dan een ConcurrentHashMap.

Maar ik ben het met jullie eens dat performance daar geen goed (en waarschijnlijk ook geen geldig) argument voor is en dat de TS zich moet realiseren dat er nog steeds concurrency issues rondom het op de queue plaatsen van requests zullen zijn.
Alarmnummer schreef op donderdag 10 mei 2007 @ 09:43:
Ik probeer het achterliggende probleem van de TS aan de kaak te stellen: hij probeert direct naar gecompliceerdere oplossingen te grijpen terwijl hij niet weet of de simpele oplossing voldoende is.
Hmmm, mij leek het juist een simpelere oplossing.

[ Voor 24% gewijzigd door Confusion op 10-05-2007 11:39 ]

Wie trösten wir uns, die Mörder aller Mörder?


  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

Alarmnummer schreef op donderdag 10 mei 2007 @ 09:43:
De concurrency connaisseurs zullen misschien het probleem al zien.
Ben ik niet, maar volgens mij doel je op iets dat ik probeerde te ondervangen met mijn laatste opmerking "en er geen risico op 'gedeeltelijke' informatie is ". Dit noem je een geval van unsafe publication toch? Het probleem is dat het niet gegarandeerd is dat de SomeEvent instantie 'volledig geconstrueerd' (ben de goede term daar even voor kwijt) is?

Wie trösten wir uns, die Mörder aller Mörder?


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Confusion schreef op donderdag 10 mei 2007 @ 11:43:
[...]

Ben ik niet, maar volgens mij doel je op iets dat ik probeerde te ondervangen met mijn laatste opmerking "en er geen risico op 'gedeeltelijke' informatie is ". Dit noem je een geval van unsafe publication toch?
correctomundo.
Het probleem is dat het niet gegarandeerd is dat de SomeEvent instantie 'volledig geconstrueerd' (ben de goede term daar even voor kwijt) is?
door reordering en visibility problemen hoeft een andere thread idd de wijzigingen niet te zien die 1 thread heeft veroorzaakt. Onder vm vanaf 1.5 heb je hier trouwens geen last meer van.
Hmmm, mij leek het juist een simpelere oplossing.
Ik doelde voornamelijk op de queue oplossing als early optimization probleem. Ik gebruik zelf ook regelmatig volatile variables zonder andere vorm van synchronizatie.

[ Voor 14% gewijzigd door Alarmnummer op 10-05-2007 15:12 ]


  • Cavalera125
  • Registratie: December 2003
  • Laatst online: 26-11 07:25
Alarmnummer schreef op donderdag 10 mei 2007 @ 15:10:
door reordering en visibility problemen hoeft een andere thread idd de wijzigingen niet te zien die 1 thread heeft veroorzaakt. Onder vm vanaf 1.5 heb je hier trouwens geen last meer van.
offtopic:
Heb je misschien wat meer info over waarom 1.5 dat niet doet, een linkje ofzo?
Pagina: 1