[win32] Code uitvoeren in GUI-thread

Pagina: 1
Acties:

  • MisterData
  • Registratie: September 2001
  • Laatst online: 20-02 17:08
Ik heb een programma dat gebruik maakt van meerdere threads. Die threads spelen elk bijvoorbeeld een mediabestand af. Nou wil ik de gebruiker op een nette manier laten weten als een bestand niet gevonden kan worden door een thread. Dus heb ik iets gemaakt waarmee middels popup-windows op het mainscherm (vergelijk het met de MSN-popups) een melding wordt gegeven, die weggeklikt kan worden door de gebruiker, maar de gebruiker niet onderbreekt bij wat 'ie aan het doen is:

Afbeeldingslocatie: http://dev.mycms.nl/notification.jpg

Vanuit de GUI-thread werkt het allemaal prima; 'notifications' maken en laten zien is geen probleem. Maar zodra een andere thread dat probeert gaat het fout; het programma loopt vast, terwijl het geen CPU gebruikt, dus ik vermoed dat er ergens een deadlock is wat betreft window messages (ik kan me geen threadlock van mezelf bedenken die dit probleem zou kunnen veroorzaken) logisch ook, want windows creeëren in een andere thread gaat sowieso niet helemaal lekker (en de afspeel-threads hebben in dit geval geen message-loop).

Is het op een simpele manier (en dan niet zelfbedachte messages sturen ofzo) om code uit te laten voeren door de GUI-thread zoals dat in Java kan met bijvoorbeeld SwingUtilities.invokeAndWait? Of wat is volgens jullie de beste manier om dit probleem op te lossen? :)

[ Voor 4% gewijzigd door MisterData op 28-05-2006 00:22 ]


  • NMe
  • Registratie: Februari 2004
  • Laatst online: 22-01 23:51

NMe

Quia Ego Sic Dico.

Met welke taal/IDE werk je precies? Ik weet in elk geval dat je bij Borland (C++ Builder en Delphi) gebruik moet maken van een Synchronize-functie, die ervoor zorgt dat de functie die je als parameter meegeeft in de GUI-thread wordt uitgevoerd.

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


  • MisterData
  • Registratie: September 2001
  • Laatst online: 20-02 17:08
Ik werk gewoon met de win32-API, ik heb er wat eigen wrapper-classes overheen gebouwd. In de win32-api heb ik niets kunnen vinden dat zou kunnen doen wat ik wil...

Verwijderd

Windows Messages zouden moeten werken, maar volgens mij is de Borland Synchronize functie ook niet meer dan een wrapper om standaard windows api functionaliteit.

[ Voor 62% gewijzigd door Verwijderd op 28-05-2006 11:07 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 21:21
Ik denk ook dat je met PostMessage iets kunt posten in de UI thread.
Ik heb ff naar de .NET Invoke method gekeken mbhv Reflector, en die maakt oa ook gebruik van PostMessage.

https://fgheysels.github.io/


Verwijderd

Ik heb voor m'n werk een applicatie in onderhoud waar we tegen dezelfde problematiek aan liepen. Om dit op te lossen hebben we een aantal custom messages gedefinieerd (WM_USER + getal) welke we naar het hoofdvenster sturen op het moment dat er wat GUI-achtigs gedaan moet worden (zoals het bijwerken van een progress bar control in de status balk).
Dus... postmessage/sendmessage en custom messages is inderdaad een goed werkende oplossing.

  • MisterData
  • Registratie: September 2001
  • Laatst online: 20-02 17:08
Nouja ik heb het niet zo op messages. Ik hou het liever binnen mijn eigen applicatie; omdat ik met reference counting werkt is het versturen van pointers via messages sowieso al problematisch en zo zijn er nog wel meer problemen (plus dat ik in m'n achterhoofd heb dat ik het ooit misschien nog wil kunnen porten). Daarom ben ik nu bezig om het op te lossen met een speciale message-loop die niet alleen op messages kan wachten (standaard GetMessage() verhaal) maar ook op een event (met MsgWaitForMultipleObjects. Dat event moet dan geset worden als er iets moet gebeuren :) Hier vind je daar dan een voorbeeld van :)

Verwijderd

Zodra je rechtsstreeks met de windows API gaat werken, kun je ideeën over het toekomstig porteren naar andere OSen redelijk ver weg stoppen. Je zult dan namelijk sowieso veel code moeten wijzigen. Vind je portabiliteit belangrijk moet je voor bibliotheken kiezen die multiplatform zijn (zoals bv QT)

Daarnaast vraag ik me af of waitformultipeobjects zo'n goede oplossing is. Het wacht inderdaad op een event en handelt die af. Als je het in je GUI thread uitvoert staat je GUI thread dus te wachten.

Een andere optie zijn Critical sections of mutexen.

Dat werkt ongeveer zo

- InitialiseCriticalSection()
- Roep een synchrone functie aan in de Gui thread die de tekstberichten weergeeft
- LeaveCriticalSection()

Als twee threads beiden een bericht willen weergeven zal de tweede thread pas starten als de eerste de criticalSection verlaten heeft. Toon je geen bericht op het scherm is je GUI gewoon responsive.

Lijkt me toch een stukje simpeler

  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Verwijderd schreef op zondag 28 mei 2006 @ 11:06:
Windows Messages zouden moeten werken, maar volgens mij is de Borland Synchronize functie ook niet meer dan een wrapper om standaard windows api functionaliteit.
Borland's Synchronize doet het gewoon met windows messages.

We adore chaos because we like to restore order - M.C. Escher


  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

kijk toch maar eens naar PostMessage en SendMessage.

minstens 1 ervan is synchroon. maw je worker-thread wacht tot je GUI-thread het bericht heeft afgehandeld. Reference counting is dus geen enkel probleem.

ASSUME makes an ASS out of U and ME


  • MisterData
  • Registratie: September 2001
  • Laatst online: 20-02 17:08
Ik heb nu dit, en het werkt redelijk goed. Het had inderdaad ook met messages gekund, maarja, het werkt nu goed zo:

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
Kort samengevat:
void AddAction(ref<actie> a) {
 {
   threadlock lock(_lock);
   _acties.push_back(a);
}
  SetEvent(_actionEvent);
}

In de msg loop:

    while(true) {
        DWORD result; 
        MSG msg; 

        while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { 
            // pending queued actions will not be executed after WM_QUIT
            if(msg.message==WM_QUIT) { 
                return;
            }
            _app->Message(msg);
        }

        HANDLE handles[1] = { _actionEvent.GetHandle() };
        result = MsgWaitForMultipleObjects(1, handles, FALSE, 1000, QS_ALLINPUT|QS_ALLPOSTMESSAGE); 

        // The result tells us the type of event we have.
        if(result==(WAIT_OBJECT_0 + 1)) {
            // New messages have arrived
            continue;
        } 
        else if(result==WAIT_ABANDONED) {
            continue;
        }
        else { 
            // process actions
            ProcessActions();
        }
    }


En ProcessActions locked de container dan weer, kopieert 'em, leegt 'em, released z'n lock en gaat daarna de acties uitvoeren (wel opletten, als je de lock niet released dan krijg je problemen als zo'n actie zelf een actie wil queuen... daarom ook moet de container gekopieerd worden). :)

Oh nog iets bij bovenstaande code: bij een WM_QUIT worden eventuele acties die nog bestaan dus niet uitgevoerd, maar dat is eenvoudig om te bouwen door ook daar even ProcessActions aan te roepen :)

In reactie op Jan Klaassen trouwens: als ik het ooit wil porten naar zo'n library als Qt dan kan ik niet rechtstreeks bij de message logica, maar wel bij threading spul. Daarom schrijf ik het liever zo, want anders kan ik er straks opnieuw over ga nadenken om het van message-based naar event-based om te bouwen. Daarnaast is een lock niet genoeg, omdat het probleem niet zit in het twee threads die tegelijk een bericht neer willen zetten, maar dat Windows blijkbaar wil dat child windows gemaakt worden door dezelfde thread als de parent. Dat los je dus niet met een simpele critical section op. Daarnaast hoeft de GUI-thread niet te wachten op de event, omdat de MsgWaitForMultipleObjects ook returned als er een message binnenkomt: je checkt dus tegelijkertijd je events en de message queue (wat GetMessage dus doet) :)

[ Voor 36% gewijzigd door MisterData op 28-05-2006 13:58 ]

Pagina: 1