Toon posts:

[c++ linux, posix threads] Threads binnen klasse?

Pagina: 1
Acties:

Verwijderd

Topicstarter
Beste mensen,

Ik ben nu al de hele ochtend bezig met Posix Threads voor mijn applicatie (C++), maar ik krijg telkens deze error:
code:
1
2
smtpserver.cpp: In constructor `SMTPServer::SMTPServer()':
smtpserver.cpp:123: error: argument of type `void*(SMTPServer::)(void*)' does not match `void*(*)(void*)'


Wat ik probeer, is het volgende:
code:
1
2
3
4
5
6
7
8
9
10
#include <pthread.h>
void* SMTPServer::filterThreadFunction(void* ptr);

void* SMTPServer::filterThreadFunction(void *ptr){
    fprintf(stderr, "filterThreadFunction started!");
}

SMTPServer::SMTPServer(){
    pthread_create(&filterThread, NULL, &filterThreadFunction, (void *)"thread1");
}


Terwijl ik een aantal Tutorials/HowTo's gelezen en gevolgd heb. Het vreemde is (vind ik), dat dit wel werkt:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <pthread.h>
#include <stdio.h>

void *test(void* arg){
    printf("%s\n", (char*)arg);
    return NULL;
}

int main (void){
    pthread_t t1;
    pthread_create(&t1, NULL, &test, (void *)"thread1");

    pthread_join(t1,NULL);
    return 0;
}


Het enige verschil lijkt mij, dat het eerste plaatsvindt binnen eigen classe, en het tweede niet... Kan iemand mij vertellen wat de fout is die ik maak?

[ Voor 6% gewijzigd door Verwijderd op 24-06-2004 12:34 ]


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

Een memberfunctie is heel wat anders dan een global of static method... een member functie kan namelijk niet opereren zonder een instance, omdat z'n this anders niet bekend is. Dat staat ook exact in de error die je krijgt:
code:
1
argument of type `void*(SMTPServer::)(void*)' does not match `void*(*)(void*)

Letterlijk vertaald: "ik wil als argument een functie pointer die een void* krijgt en ook retourneert, en jij geeft me een memberfunctie van SMTPServer die een void* krijgt en ook retourneert, en dat kan ik niet vertalen"

Ergo je moet die callback static maken als je 'm binnen een class plaatst, omdat ie dan niet meer instancebound is.

Professionele website nodig?


Verwijderd

Topicstarter
Je hebt helemaal gelijk. De code wordt dus nu:
code:
1
2
3
4
5
6
7
8
9
10
#include <pthread.h>
static void* SMTPServer::filterThreadFunction(void* ptr);

void* SMTPServer::filterThreadFunction(void *ptr){
    fprintf(stderr, "filterThreadFunction started!");
}

SMTPServer::SMTPServer(){
    pthread_create(&filterThread, NULL, &filterThreadFunction, (void *)"thread1");
}



Pfff... Daar was ik echt nooit zelf achter gekomen! Ik ben nog niet heel erg gevorderd met C++, maar nu heb ik er weer wat bijgeleerd :P! Bedankt!

  • 12_0_13
  • Registratie: April 2004
  • Laatst online: 12-02 13:19
Veel mooier is een thread klasse te maken:

C++:
1
2
3
4
5
6
7
8
9
10
/**
 * Class helper for pthread threads. 
 */
class AbstractThread  
{
public:
    AbstractThread();
    virtual ~AbstractThread();
    virtual void *run(void)  =0;
};


C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/** 
 *  This function is a helper function. It has normal C linkage, and is
 *  as the base for newly created AbstractThread objects. It runs the
 *  run method on the AbstractThread object passed to it (as a void *).
 *  After the AbstractThread method completes normally (i.e returns),
 *  we delete the object.
 */ 
void *ThreadStartup(void *_tgtObject) 
{
    AbstractThread *tgtObject = (AbstractThread *)_tgtObject;
    //printf("Running thread object in a new thread\n");

    void *threadResult = tgtObject->run();
    //printf("Deleting object\n");

    delete tgtObject;
    return threadResult;
}


en in je main:
C++:
1
rc = pthread_create(&thread1, NULL, ThreadStartup, myOwnThreadInstance);

[ Voor 19% gewijzigd door 12_0_13 op 24-06-2004 13:18 ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 04:19
Je hoeft er trouwens geen member function van te maken. Als je een Java-achtergrond hebt heb je daar misschien helemaal niet over nagedacht. Voordeel van een losse (lokale) functie is dat je 'm niet in je klassedeclaraties hoeft op te nemen (en die blijft daardoor helderder en meer toegespitst op de werkelijke functionaliteit); nadeel is dat je dan geen non-public members van je klasse kunt gebruiken.

De overweging is afhankelijk van wat er daadwerkelijk in je threadfunctie gaat gebeuren. Als je uitsluitend van de public interface van je klasse gebruik gaat maken (of helemaal niets met die klasse doet) dan is het te overwegen om de er een losse functie van te maken, zodat de functie ook conceptueel ontkoppelt is van de klasse.

Verwijderd

12_0_13 schreef op 24 juni 2004 @ 13:16:
Veel mooier is een thread klasse te maken:
Het idee is goed, maar je code vind ik niet heel fraai en gaat kapot als je thread instance variabele niet ooit met 'new' is aangemaakt (omdat je hem expliciet 'delete' in je functie), bijvoorbeeld als het een member variabele is van een andere class ofzo.
edit:
Zie ook soultaker hier direct onder :)

[ Voor 10% gewijzigd door Verwijderd op 24-06-2004 13:29 ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 04:19
12_0_13 schreef op 24 juni 2004 @ 13:16:
Veel mooier is een thread klasse te maken:
Op zichzelf wel, maar jouw suggestie laat nogal te wensen over. Je delete objecten die niet weg moeten en uiteindelijk encapsuleer je de daadwerkelijke pthread calls niet. De suggestie om een abstract base class te maken is wel zinnig.

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

Soultaker schreef op 24 juni 2004 @ 13:20:
Je hoeft er trouwens geen member function van te maken. Als je een Java-achtergrond hebt heb je daar misschien helemaal niet over nagedacht. Voordeel van een losse (lokale) functie is dat je 'm niet in je klassedeclaraties hoeft op te nemen (en die blijft daardoor helderder en meer toegespitst op de werkelijke functionaliteit); nadeel is dat je dan geen non-public members van je klasse kunt gebruiken.

De overweging is afhankelijk van wat er daadwerkelijk in je threadfunctie gaat gebeuren. Als je uitsluitend van de public interface van je klasse gebruik gaat maken (of helemaal niets met die klasse doet) dan is het te overwegen om de er een losse functie van te maken, zodat de functie ook conceptueel ontkoppelt is van de klasse.
Helaas klopt dat OO-technisch voor geen meter imho ;)

De callback is per definitie intern en hoort dus private te zijn, wat juist het grootste argument is voor een static callback member.

Professionele website nodig?


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 04:19
curry684 schreef op 24 juni 2004 @ 13:35:
Helaas klopt dat OO-technisch voor geen meter imho ;)
C++ is dan ook een taal voor meerdere paradigma's. Een methode van een object zou een kernachtige taak uit moeten voeren op basis van de staat van dat object; als de taak is uitgevoerd retourneert de methode. Je kunt methoden dan meestal ook oneindig vaak en op willekeurige momenten aanroepen. Typische threadfuncties hebben meestal geen afgeronde taakomschrijven, zijn vaak gebonden aan specifieke gebruiksvoorschriften, en bestaan slechts om de realtime eigenschappen van het systeem in goede banen te leiden (denk bijvoorbeeld aan typische threadprocedures die alle binnenkomende connecties accepteren).

Voor dit doel is het naar mijn idee beter om van het OO paradigma af te stappen en een 'lokale' functie te definiëren, juist om de verwarring met typische OO methodes te voorkomen. Uiteraard is het niet de bedoeling dat die functie vervolgens geëxporteerd wordt; hij blijft voor intern gebruik.

Maar zoals ik al aan probeerde te geven met de voorzichtige formulering is het een kwestie van smaak en de manier waarop je het liefst met paradigma's en C++ omgaat. Ik wilde slechts aangeven dat er meer dan één manier is om er tegen aan te kijken. (Net zoals het starten van een threadfunctie en het aanroepen van een methode op een threadobject twee conceptueel verschillende methoden zijn om multithreaded code uit te voeren.)

[ Voor 20% gewijzigd door Soultaker op 24-06-2004 13:49 ]


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

Inderdaad doet de callback bij mij ook nooit meer dan het argument terugcasten naar een instance en een (pure) virtual protected Execute method binnen de class zelf aanroepen :) Dat is wmb iig de cleanste oplossing.

Professionele website nodig?


  • 12_0_13
  • Registratie: April 2004
  • Laatst online: 12-02 13:19
Hey, het was ook maar een stukje copy-paste vanuit een programma waar in thread in gebruik :)

Ik geef toe dat het deleten eigenlijk niet zo netjes is in die functie, ik zal het meteen even aanpassen.

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
OO en threads is sowieso moeilijk, maar het wordt pas echt pijnlijk als je een non-OO threads package gebruikt. pthreads is een C-standaard, geen C++. Dat is uiteindelijk de reden dat je geen member functions kunt aanroepen; C heeft die nou eenmaal niet.

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

12_0_13 schreef op 24 juni 2004 @ 13:16:
Veel mooier is een thread klasse te maken:

C++:
1
blaat
Zoiets lijkt me beter:
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
29
30
31
32
33
34
35
36
37
38
39
class CThread
{
    private:
        static void ThreadFunc(void* lpf)
        {
            try
            {
                (reinterpret_cast<CThread *>(lpf))->Run();
            }
            catch(...)
            {
                throw std::runtime_error("Unhandled thread exception.");
            }
        }

    public:
        CThread() {}

        virtual ~CThread() {}

        static void Sleep(long lMilliSeconds = 1)
        {
            ::sleep(lMilliSeconds);
        }

        int Start()
        {
            return ::pthread_create(NULL, NULL, ThreadFunc, this);
        }

        virtual void Run() = 0;
};

class CMyClass : public CThread
{
        public:
                void Run() { while(1) doIets(); }

};

Disclaimer; komt uit de losse pols dus misschien niet direct compilebaar :P

Verwijderd

Dat lijkt verdacht veel op de mijne, al is die zonder pthreads (allemaal win32 :P ) en heet 'run' bij mij 'code', omdat ik start en run wat onduidelijk vond :) . Ook heb ik er een uberluxe 'stop' functie op zitten en roept ie die ook aan in de destructor; dan heb ik nooit 'vergeten' threads lopen...

offtopic:
Wel grappig overigens is je code style. Hongaarse notatie (lMilliSeconds) is ongebruikelijk in de unix wereld. Ook je gebruik van hoofdletters is onorthodox, unix fans zijn vaak underscore_fetisjisten en HoofdletterHaters.

[ Voor 66% gewijzigd door Verwijderd op 24-06-2004 23:20 ]


Verwijderd

Verwijderd schreef op 24 juni 2004 @ 23:16:
offtopic:
Wel grappig overigens is je code style. Hongaarse notatie (lMilliSeconds) is ongebruikelijk in de unix wereld. Ook je gebruik van hoofdletters is onorthodox, unix fans zijn vaak underscore_fetisjisten en HoofdletterHaters.
De stop, pause, whatever functies mag de topicstarter zelf implementeren. Het ging mij meer om de opzet van een thread class. :P

Ik ben dan ook geen fan van de 'unix programmeerstyle' want ik vind het ronduit lelijk :P
De 'unix style' vind ik ook echt iets voor plain C code, C++ code moet in hungarian notatie, of iig iets wat er op lijkt. Maar dat is mijn mening.
En ik ben ook geen fan van functie namen met een hoofdletter, maar dit is er het afgelopen half jaar ingeslopen omdat ik een aantal fanatieke hoofdletter fanaten in mijn team had :P

[ Voor 27% gewijzigd door Verwijderd op 24-06-2004 23:48 ]


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
'stop' is altijd een lastige functie; er zijn geen thread-packages die daar een nette oplossing voor hebben. Juist daar wordt het pijnlijk duidelijk dat het C packages zijn; een thread-stop runt nooit destructors - tenzij je er handmatig code voor schrijft in elke thread, en dan ook nog in bijna alle code die zo'n thread gebruikt.

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

'Stop' is inderdaad een lastige en heeft bij de door mij gekozen oplossing ook wel wat werk nodig in de thread code zelf...

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class CThread
{
public:
    CThread(void);
    virtual ~CThread(void);

    virtual bool Init();    // Initialize local thread stuff

    bool Run();                         // Start the thread
    virtual bool Stop();    // Stop the thread
    virtual void Body() = 0;

protected:
    HANDLE m_hStopEvent;

private:
    HANDLE m_hThread;
};


De programmeur (ik meestal) schrijft dus zelf in ieder geval de functie 'Body'.

In de functie 'Init' maak ik een event object aan om te gebruiken als signaal aan de thread dat hij verwacht wordt te stoppen:
C++:
1
2
3
4
5
6
7
bool CThread::Init()
{
    m_hStopEvent = ::CreateEvent(NULL, false, false, NULL);

    if (m_hStopEvent != NULL) return true;
    return false;
}


In de functie 'stop':
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
bool CThread::Stop()
{
    if (m_hStopEvent != NULL && m_hThread != NULL)
    {
        ::SetEvent(m_hStopEvent);
        ::WaitForSingleObject(m_hThread, INFINITE);

        ::CloseHandle(m_hThread);
        m_hThread = NULL;

        return true;
    }

    return false;
}


De thread zelf wordt geacht op bepaalde tijden te kijken of het m_hStopEvent event object signalled is en als dat zo is zichzelf op te ruimen.

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

MSalters schreef op 25 juni 2004 @ 10:17:
'stop' is altijd een lastige functie; er zijn geen thread-packages die daar een nette oplossing voor hebben. Juist daar wordt het pijnlijk duidelijk dat het C packages zijn; een thread-stop runt nooit destructors - tenzij je er handmatig code voor schrijft in elke thread, en dan ook nog in bijna alle code die zo'n thread gebruikt.
Ik heb geloof ik al eens beargumenteerd dat je Stop() of Terminate() ook gewoon niet moet implementeren, en juist een base implementatie voor externe friendly termination moet aanbieden.

.NET lost dit overigens ook netjes op: de Thread.Abort() functie gooit een ThreadAbortException binnen de context van de thread, welke in principe altijd tot een abnormal shutdown van de thread zal zorgen met normale afhandeling van destructors en garbage collection. Letterlijk afschieten zit er dus gewoon niet in.

Professionele website nodig?


Verwijderd

Ik zie niet in wat er mis is met het implementeren van 'Stop', zolang de thread zelf 'weet' dat hij afgeschoten wordt (zie mijn code). Rucksichtlos een thread afknallen en daarmee alle resources die deze gereserveerd had 'kwijtraken' is altijd wel een slecht plan.

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
curry684 schreef op 25 juni 2004 @ 10:35:
[...]
.NET lost dit overigens ook netjes op: de Thread.Abort() functie gooit een ThreadAbortException binnen de context van de thread, welke in principe altijd tot een abnormal shutdown van de thread zal zorgen met normale afhandeling van destructors en garbage collection. Letterlijk afschieten zit er dus gewoon niet in.
Het is niet netjes, helaas: Om correcte code te schrijven is het nodig dat sommige fundamentele operaties nooit een exception gooien, bijvoorbeeld swap( ). ThreadAbortException maakt dit praktisch onmogelijk.

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


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 04:19
Verwijderd schreef op 25 juni 2004 @ 10:52:
Ik zie niet in wat er mis is met het implementeren van 'Stop', zolang de thread zelf 'weet' dat hij afgeschoten wordt (zie mijn code).
In jouw code wordt (gelukkig) niets afgeschoten. Je set een Event en wacht dan netjes tot die afgelopen is. Dat is fundamenteel anders dan low-level threading API's die vaak de mogelijkheid bieden om simpelweg de thread te beëindigen: er worden geen instructies meer uitgevoerd in die thread.

Jouw implementatie heeft meer weg van een 'Join' operatie, aangezien je netjes wacht totdat de thread zelf klaar is (hoewel voor het joinen met een thread het niet noodzakelijk is om ook een event te gebruiken; een thread kan ook uit zichzelf klaar zijn wanneer zijn taak is uitgevoerd).
Pagina: 1