Probleme mit Lock-Unlock-Mutex

Anfängerfragen zum Programmieren mit PureBasic.
Hugo
Beiträge: 56
Registriert: 30.03.2005 12:07
Wohnort: Nähe München

Probleme mit Lock-Unlock-Mutex

Beitrag von Hugo »

Hallo,

nach langer Zeit versuche ich mich mal wieder mit der Programmierung.

Dabei ist mein Ziel, ein kleines Programm mit Threads zu schreiben.

Da das Hauptprogramm und die Threads auf gemeinsame Resourcen zugreifen sollen, habe ich mit Mutex experimentiert und habe da ein mir nicht verständliches Problem festgestellt.

Wenn ich dieses Programm (mit Debugger und Threadsichere Exe) laufen lasse, wird der Mutex, sobald das Hautprogamm ihn auch anfordert nicht wieder frei gegeben.

Sobad ich den Lock im Hauptprogramm oder den SetGadgetText in den Thread herausnehme geht alles.

Vielleicht kann mir ja jemand dabei helfen, herauszufinden, was ich falsch mache.

Code: Alles auswählen

;- Mit   Debugger on   und  Threadsichere Exe <<<<--------

Global Thread1.l, Thread2.l, start.l, Mutex1.l = CreateMutex()                       

#string1 = 1
#string2 = 2

Procedure  Thread1()
  Repeat
    Debug "request Mutex1-Thread1"
    LockMutex( Mutex1 )
    Debug "lock Mutex1-Thread1 ok"
    SetGadgetText( #string1, FormatDate( "%hh:%ii:%ss", Date() ) )
    UnlockMutex( Mutex1 )
    Debug "unlock Mutex1-Thread1"
    Delay( 50 )
  ForEver
EndProcedure

Procedure  Thread2()
  Repeat
    Debug "request Mutex1-Thread2"
    LockMutex( Mutex1 )
    Debug "lock Mutex1-Thread2 ok"
    SetGadgetText( #string2, FormatDate( "%hh:%ii:%ss", Date() ) )
    UnlockMutex( Mutex1 )
    Debug "unlock Mutex1-Thread2"
    Delay( 50 )
  ForEver
EndProcedure


If OpenWindow( 0, 0,0, 300, 100, "test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered ) And CreateGadgetList( WindowID( 0 ) )
  StringGadget( #string1,  10, 10, 100, 20, "" )
  StringGadget( #string2, 120, 10, 100, 20, "" )
    
  Thread1 = CreateThread( @Thread1(), 0 )
  Thread2 = CreateThread( @Thread1(), 0 )
  start   = ElapsedMilliseconds() + 2000
  
  Repeat
    If start < ElapsedMilliseconds()
      Debug "request Mutex1-Main"
      LockMutex( Mutex1 )
      Debug "lock Mutex1-Main ok"
      UnlockMutex( Mutex1 )
      Debug "unlock Mutex1-Main"
    EndIf
  Until WaitWindowEvent( 100 ) = #PB_Event_CloseWindow
  
  KillThread( Thread1 )
  KillThread( Thread2 )
  CloseWindow( 0 )
EndIf

Ich verwende PB 4.02.

Vielen Dank,
Hugo
Benutzeravatar
tobe
Beiträge: 146
Registriert: 14.09.2006 17:33
Wohnort: Oktoberfest

Beitrag von tobe »

hi Hugo,

mir ist aufgefallen das deine threads keinen parameter haben,
das kann fehler erzeugen.
und beim CreateThread() startest du zwei mal Thread1(),
aber vielleicht soll das so sein.
PureBasic 4.40 (Windows - x86)
Benutzeravatar
remi_meier
Beiträge: 1078
Registriert: 29.08.2004 20:11
Wohnort: Schweiz

Beitrag von remi_meier »

Das hier scheint das Problem zu lösen:

Code: Alles auswählen

Until LockMutex(Mutex1) And  WaitWindowEvent(  ) = #PB_Event_CloseWindow And UnlockMutex(Mutex1)
Muss WaitWindowEvent() auch geschützt werden, wenn ein Gadget geändert
wird? :?

Edit: wobei ich feststelle, dass mein Code natürlich total falsch ist :|
Benutzeravatar
remi_meier
Beiträge: 1078
Registriert: 29.08.2004 20:11
Wohnort: Schweiz

Beitrag von remi_meier »

Ich habe festgestellt, dass jede Gadget-Manipulation in einem Thread
auch genau einen Aufruf von (Wait)WindowEvent() erfordert, ansonsten
wird der Thread blockiert :|
Wenn ich also SetGadgetText() im Thread aufrufe, muss im Hauptthread
genau einmal WindowEvent() aufgerufen werden.
Das ist auch dein Problem, aber wieso ist das so?
Benutzeravatar
remi_meier
Beiträge: 1078
Registriert: 29.08.2004 20:11
Wohnort: Schweiz

Beitrag von remi_meier »

Ok, SendMessage_() erfordert ja, dass die Message gleich verarbeitet
wird, was natürlich nur von WindowEvent() gemacht wird :freak:
Blöde Sache...
freak
PureBasic Team
Beiträge: 766
Registriert: 29.08.2004 00:20
Wohnort: Stuttgart

Beitrag von freak »

Die Message-Kommunikation unter Windows läuft immer synchron ab.
Wenn also ein Thread an einen anderen eine Message schickt,
dann blockiert das bis der Zielthread Nachrichten verarbeitet.
(was even in WaitWindowEvent(), oder auch in einigen API Befehlen passiert)

Zum setzen des Gadget textes muss man unweigerlich Nachrichten versenden,
desshalb lässt sich das nicht verhindern.

Ich würde im Hauptthread einfach nur die Event-Verarbeitung machen und
den Rest den Threads überlassen. Damit ist auch gleich noch garantiert
das User-Events prompt verarbeitet werden.
Ansonsten muss man halt einfach ein bischen aufpassen um deadlocks zu vermeiden.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8809
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Beitrag von NicTheQuick »

Lustig, dass ich das Problem gerade gestern auch hatte.
Da ich es mir aber nicht erklären konnte und auch zu faul war
herumzuexperimentieren, weil es relativ komplex war, hab ich es doch
wieder anders und ohne Threads gelöst.

Aber würde es denn funktionieren, wenn man in einem Thread das Fenster
erstellt und dann per GetMessage(), TranslateMessage() und
DispatchMessage() im Thread arbeitet, dazu noch ein Windowcallback und
(Wait)WindowEvent() komplett außen vor lässt?
freak
PureBasic Team
Beiträge: 766
Registriert: 29.08.2004 00:20
Wohnort: Stuttgart

Beitrag von freak »

Gleiches Ergebnis. Windows macht die Synchronisation, nicht PB.
Das ist auch sinnvoll, sonst müsste man ja auch in nicht-thread Programmen
den WindowCallback threadsicher machen falls Events asynchron ankommen.
Hugo
Beiträge: 56
Registriert: 30.03.2005 12:07
Wohnort: Nähe München

Beitrag von Hugo »

Vielen Dank für eure Antworten.

Aber ich habe das Ganze noch nicht so richtig verstanden.

Was muss ich denn nun tun, um den Code zum Laufen zu bekommen?
Vielleicht hat ja jemand ein kleines Beispiel?

Vielen Dank nochmals,
Hugo
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8809
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Beitrag von NicTheQuick »

@freak:
Ist der folgende Code dann richtig oder falsch?

Code: Alles auswählen

Global Quit.l = #False
Procedure WinThread(nr.l)
  Protected WinID.l, GadID.l, c = 0
  
  WinID = OpenWindow(#PB_Any, nr * 16, nr * 16, 400, 300, "Fenster " + Str(nr), #PB_Window_SystemMenu)
  If WinID
    If CreateGadgetList(WindowID(WinID))
      GadID = ButtonGadget(#PB_Any, 350, 280, 50, 20, "Start", #PB_Button_Toggle)
      
      Repeat
        Select WaitWindowEvent(50)
          Case #PB_Event_CloseWindow
            Break
          
          Case #PB_Event_Gadget
            If EventGadget() = GadID
              If GetGadgetState(GadID) = 0
                c = 0
              Else
                c = 1
              EndIf
            EndIf
          
          Case 0
            If c
              c + 1
              SetGadgetText(GadID, "Stop " + Str(c))
            EndIf
        EndSelect
      Until Quit
    EndIf
    CloseWindow(WinID)
  EndIf
EndProcedure

Dim Thread.l(4)
For a = 0 To 4
  Thread(a) = CreateThread(@WinThread(), a + 1)
Next

Repeat : Delay(10) : Until GetAsyncKeyState_(#VK_ESCAPE)
Quit = #True

For a = 0 To 4
  WaitThread(Thread(a))
Next
Der Zähler läuft nur noch, wenn man nichts im Fenster macht.
Antworten