Seite 1 von 2

Probleme mit Lock-Unlock-Mutex

Verfasst: 16.01.2007 13:24
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

Verfasst: 16.01.2007 15:57
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.

Verfasst: 16.01.2007 17:48
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 :|

Verfasst: 16.01.2007 18:36
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?

Verfasst: 16.01.2007 18:59
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...

Verfasst: 16.01.2007 19:02
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.

Verfasst: 16.01.2007 21:12
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?

Verfasst: 16.01.2007 21:44
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.

Verfasst: 17.01.2007 08:23
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

Verfasst: 17.01.2007 12:05
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.