Datenbank Threads

In dieser Linux-Ecke dürfen nur Themen rund um Linux geschrieben werden.
Beiträge, die plattformübergreifend sind, gehören ins 'Allgemein'-Forum.
ccode_new
Beiträge: 1214
Registriert: 27.11.2016 18:13
Wohnort: Erzgebirge

Datenbank Threads

Beitrag von ccode_new »

Hi Leute,

dies wird ein weiteres Thread-Thema.

Was mich noch brennend interessiert ist:

Ist es irgend wie möglich MySql / SQLLite -Datenbankanweisungen und GUI (GTK) -Zugriff gleichzeitig zu machen ?

_______________________________
Unter Windows ist das ja kein Problem, nur läuft der Datenbankzugriff immer im gleichen Thread wie der GUI-Zugriff.

Hab bis jetzt noch keine Lösung für das Problem gefunden.

Danke für jede hilfreiche Antwort.
Betriebssysteme: div. Windows, Linux, Unix - Systeme

no Keyboard, press any key
no mouse, you need a cat
Benutzeravatar
mk-soft
Beiträge: 3695
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Datenbank Threads

Beitrag von mk-soft »

Geht über den Umweg PostEvent oder über das Modul ThreadToGUI.

Link: http://www.purebasic.fr/german/viewtopi ... =8&t=29728

GetGadgetText, GetGadgetXY geht auch aus einem Thread. Änderungen an Gadget immer an den Mainscope senden

P.S. Wie man Threads steuern könnte kann man sich hier anschauen.
Link: http://www.purebasic.fr/german/viewtopi ... ol#p330743
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
ccode_new
Beiträge: 1214
Registriert: 27.11.2016 18:13
Wohnort: Erzgebirge

Re: Datenbank Threads

Beitrag von ccode_new »

Hi mk-soft !

Irgend wie funktioniert das mit PostEvent oder ThreadToGUI mit SQLLite nicht, oder ich mache etwas falsch.

Bei mir friert das Fenster immer ein und meldet sich nach Erfolg wieder.

Außerdem kann ich keine Datenbankbefehle in einem Extra-Thread nutzen.

Da stürzt PB ab.

Ich werde aber noch ein bisschen probieren.

Unter Windows gibt da ja keine Probleme, aber Linux macht da mal wieder Ärger.

Außerdem sind die SQL-Abfragen unter Linux mindestens doppelt bis dreimal so langsam wie unter Windows 10.

Getestet unter 2 verschiedene Systeme und Linux-Distros.
Betriebssysteme: div. Windows, Linux, Unix - Systeme

no Keyboard, press any key
no mouse, you need a cat
ccode_new
Beiträge: 1214
Registriert: 27.11.2016 18:13
Wohnort: Erzgebirge

Re: Datenbank Threads

Beitrag von ccode_new »

Wie könnte ich hier die Statusbarabfrage asynchron machen?

Code: Alles auswählen

  Protected.s SQL1, SQL2, sql9, id_name
  Protected size, *mem, size2, *mem2
  
  If CreateFile(1, fili$)
    CloseFile(1)
    If OpenDatabase(#CopyDB, fili$, "", "")
      
      SQL1 = "CREATE TABLE datenbank (id INTEGER PRIMARY KEY, name TEXT, gruppe TEXT, version TEXT, beschreibung MEMO, tabelle longblob, picname TEXT, fotodata longblob, extdata longblob, extname TEXT)"
      
      c = 0
      v.f = 0
      
      If DatabaseUpdate(#CopyDB, SQL1) = #False
        Debug DatabaseError()
      EndIf
      If DatabaseQuery(#DB, "SELECT * FROM datenbank ORDER BY gruppe")
        While NextDatabaseRow(#DB)
          id_name = GetDatabaseString(#DB, 1)
          
          size = DatabaseColumnSize(#DB, 7)
          size2 = DatabaseColumnSize(#DB, 8)
          Debug size
          If size And Not size2
            *mem = AllocateMemory(size)
            If *mem
              SQL2 = "INSERT INTO datenbank (name, gruppe, version, beschreibung, tabelle, picname, fotodata) "
              SQL2 + "VALUES ('"
              SQL2 + id_name + "','"
              SQL2 + GetDatabaseString(#DB, 2) + "','"
              SQL2 + GetDatabaseString(#DB, 3) + "','"
              SQL2 + GetDatabaseString(#DB, 4) + "','"
              SQL2 + GetDatabaseString(#DB, 5) + "','"
              SQL2 + GetDatabaseString(#DB, 6) + "', "
              SQL2 + "? )"
              If GetDatabaseBlob(#DB, #row_FotoData, *mem, size)
                SetDatabaseBlob(#CopyDB, 0, *mem, size)
                DatabaseUpdate(#CopyDB, SQL2)
                FreeMemory(*mem)
              EndIf
            EndIf
          ElseIf size2 And Not size
            *mem2 = AllocateMemory(size2)
            If *mem2
              SQL2 = "INSERT INTO datenbank (name, gruppe, version, beschreibung, tabelle, picname, extdata, extname) "
              SQL2 + "VALUES ('"
              SQL2 + id_name + "','"
              SQL2 + GetDatabaseString(#DB, 2) + "','"
              SQL2 + GetDatabaseString(#DB, 3) + "','"
              SQL2 + GetDatabaseString(#DB, 4) + "','"
              SQL2 + GetDatabaseString(#DB, 5) + "','"
              SQL2 + GetDatabaseString(#DB, 6) + "', "
              SQL2 + "? ,'"
              SQL2 + GetDatabaseString(#DB, 9) + "')"
              
              If GetDatabaseBlob(#DB, 8, *mem2, size2)
                SetDatabaseBlob(#CopyDB, 0, *mem2, size2)
                DatabaseUpdate(#CopyDB, SQL2)
                FreeMemory(*mem2)
              EndIf
            EndIf
          ElseIf size And size2
            *mem = AllocateMemory(size)
            *mem2 = AllocateMemory(size2)
            If *mem And *mem2
              SQL2 = "INSERT INTO datenbank (name, gruppe, version, beschreibung, tabelle, picname, fotodata, extdata, extname) "
              SQL2 + "VALUES ('"
              SQL2 + id_name + "','"
              SQL2 + GetDatabaseString(#DB, 2) + "','"
              SQL2 + GetDatabaseString(#DB, 3) + "','"
              SQL2 + GetDatabaseString(#DB, 4) + "','"
              SQL2 + GetDatabaseString(#DB, 5) + "','"
              SQL2 + GetDatabaseString(#DB, 6) + "', "
              SQL2 + "? , ? ,'"
              SQL2 + GetDatabaseString(#DB, 9) + "')"
              
              If GetDatabaseBlob(#DB, 7, *mem, size) And GetDatabaseBlob(#DB, 8, *mem2, size2)
                SetDatabaseBlob(#CopyDB, 0, *mem, size)
                SetDatabaseBlob(#CopyDB, 1, *mem2, size2)
                DatabaseUpdate(#CopyDB, SQL2)
                FreeMemory(*mem)
                FreeMemory(*mem2)
              EndIf
              ; Mit GetDatabaseBlob laden wir nun die Bilddaten in unseren Speicher.
            EndIf
          Else
            SQL2 = "INSERT INTO datenbank (name, gruppe, version, beschreibung, tabelle, picname) "
            SQL2 + "VALUES ('"
            SQL2 + id_name + "','"
            SQL2 + GetDatabaseString(#DB, 2) + "','"
            SQL2 + GetDatabaseString(#DB, 3) + "','"
            SQL2 + GetDatabaseString(#DB, 4) + "','"
            SQL2 + GetDatabaseString(#DB, 5) + "','"
            SQL2 + GetDatabaseString(#DB, 6) + "')"
            DatabaseUpdate(#CopyDB, SQL2)
          EndIf
          c + 1
          v = 100 / CountGadgetItems(#gad_ListView) * c
          StatusBarProgress(#Statusbar, 1, Int(v), #PB_StatusBar_Raised) ;Wie kann ich das asynchron machen ?
        Wend
        FinishDatabaseQuery(#DB)
      Else
        Debug DatabaseError()
      EndIf
      CloseDatabase(#CopyDB)
      StatusBarText(#Statusbar, 2, "Der Export war erfolgreich.")
      MessageRequester("Programm", "Die Datenbank wurde erfolgreich exportiert!")
      ListFuellen()
    Else
      Debug DatabaseError()
      MessageRequester("Fehler", "Die Datenbank konnte nicht exportiert werden.")
      End
    EndIf
  Else
    Debug fili$ + " konnte nicht erstellt werden."
    MessageRequester("Schreib-Fehler", "Die Datenbank konnte nicht erstellt werden.")
  EndIf
Betriebssysteme: div. Windows, Linux, Unix - Systeme

no Keyboard, press any key
no mouse, you need a cat
Benutzeravatar
mk-soft
Beiträge: 3695
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Datenbank Threads

Beitrag von mk-soft »

P.S. Editiert
Erst mal die Aufgaben in Prozeduren zusammenfassen und diese dann aus einem Thread ausführen...
Siehe mein Beispiel...

Sollte so weit funktionieren.
Habe mal ein Beispiel von mir auf Thread umgebaut. Mit den Menu/Ablage/Start wird der Thread 'MyThread' gestartet...

Ist nur ein Beispiel für die Funktion 'ShowDatabaseRows(Gadget, DBase, Hide = #False)'
und jetzt als 'DoShowDatabaseRows(Gadget, DBase, Hide = #False) aus einem Thread heraus.

Läuft unter Linux und Mac

Code: Alles auswählen

;-TOP

; ***************************************************************************************

; ShowDatabaseItems by mk-soft from 12.02.2017
; DoShowDatabaseItems for threads from 04.07.2015

IncludeFile "Modul_ThreadToGUI.pb"

UseModule ThreadToGUI
  
Procedure GetTextWidth(Text.s, FontID.i = 0)
  Static image
  Protected result
  
  If Not image
    image = CreateImage(#PB_Any, 1, 1)
  EndIf
  
  If image And StartDrawing(ImageOutput(image))
    If FontID
      DrawingFont(FontID)
    EndIf
    result = TextWidth(Text)
    StopDrawing()
  EndIf
  ProcedureReturn result
EndProcedure

; ---------------------------------------------------------------------------------------

Procedure ClearGadgetColumns(Gadget)
  CompilerIf #PB_Compiler_Version <= 551
    ClearGadgetItems(Gadget)
    While GetGadgetItemText(Gadget, -1, 0)
      RemoveGadgetColumn(Gadget, 0)
    Wend
  CompilerElse
    RemoveGadgetColumn(Gadget, #PB_All)
  CompilerEndIf
EndProcedure

; ---------------------------------------------------------------------------------------

Procedure CountGadgetColumns(Gadget)
  Protected result
  CompilerIf #PB_Compiler_Version <= 551
    While GetGadgetItemText(Gadget, -1, result)
      result + 1
    Wend
  CompilerElse
    result = GetGadgetAttribute(Gadget, #PB_ListIcon_ColumnCount)
  CompilerEndIf
  ProcedureReturn result
EndProcedure

; ---------------------------------------------------------------------------------------

; ThreadToGUI: Only inside Threads
Procedure DoShowDatabaseRows(Gadget, DBase, Hide = #False)
  Protected result.i, columns.i, index.i, size.i, text.s
  
  Repeat
    If GadgetType(Gadget) <> #PB_GadgetType_ListIcon
      Break
    EndIf
    If Not IsDatabase(DBase)
      Break
    EndIf
    DoHideGadget(Gadget, Hide)
    DoClearGadgetColumns(Gadget)
    columns = DatabaseColumns(DBase)
    For index = 0 To columns - 1
      text = DatabaseColumnName(DBase, index)
      size = GetTextWidth(text) + 12
      DoAddGadgetColumn(Gadget, index, text, size)
    Next
    While NextDatabaseRow(DBase)
      text = ""
      For index = 0 To columns - 1
        text + GetDatabaseString(DBase, index) + #LF$
      Next
      DoAddGadgetItem(Gadget, -1, text)
    Wend
    FinishDatabaseQuery(DBase)
    DoHideGadget(Gadget, #False)
    DoWait() ; Wait on Mainscope for do all Events
    result = CountGadgetItems(Gadget)
  Until #True
  ProcedureReturn result
  
EndProcedure

; ---------------------------------------------------------------------------------------

Procedure ShowDatabaseRows(Gadget, DBase, Hide = #False)
  Protected result.i, columns.i, index.i, size.i, text.s
  
  Repeat
    If GadgetType(Gadget) <> #PB_GadgetType_ListIcon
      Break
    EndIf
    If Not IsDatabase(DBase)
      Break
    EndIf
    HideGadget(Gadget, Hide)
    ClearGadgetColumns(Gadget)
    columns = DatabaseColumns(DBase)
    For index = 0 To columns - 1
      text = DatabaseColumnName(DBase, index)
      size = GetTextWidth(text) + 12
      AddGadgetColumn(Gadget, index, text, size)
    Next
    While NextDatabaseRow(DBase)
      text = ""
      For index = 0 To columns - 1
        text + GetDatabaseString(DBase, index) + #LF$
      Next
      AddGadgetItem(Gadget, -1, text)
    Wend
    FinishDatabaseQuery(DBase)
    HideGadget(Gadget, #False)
    result = CountGadgetItems(Gadget)
  Until #True
  ProcedureReturn result
  
EndProcedure

; ***************************************************************************************

CompilerIf #PB_Compiler_IsMainFile
  
  ; Constant
  Enumeration ;Window
    #Main
  EndEnumeration
  
  Enumeration ; Menu
    #Menu
  EndEnumeration
  
  Enumeration ; MenuItems
    #MenuExitApplication
    #MenuStart
  EndEnumeration
  
  Enumeration ; Gadgets
    #List
    #Edit
  EndEnumeration
  
  Enumeration ; Statusbar
    #Status
  EndEnumeration
  
  ; Global Variable
  Global ExitApplication
  
  ; Functions
  
  UseSQLiteDatabase()
  
  Procedure CheckDatabaseUpdate(Database, Query$)
    Result = DatabaseUpdate(Database, Query$)
    If Result = 0
      Debug DatabaseError()
    EndIf
    ProcedureReturn Result
  EndProcedure
  
  Procedure CreateDummyDatabase(DBase)
    If OpenDatabase(DBase, ":memory:", "", "")
      CheckDatabaseUpdate(DBase, "CREATE TABLE food (recid INTEGER PRIMARY KEY ASC, name CHAR(50), weight FLOAT)")
      CheckDatabaseUpdate(DBase, "INSERT INTO food (name, weight) VALUES ('apple', '10.005')")
      CheckDatabaseUpdate(DBase, "INSERT INTO food (name, weight) VALUES ('pear', '5.9')")
      CheckDatabaseUpdate(DBase, "INSERT INTO food (name, weight) VALUES ('banana', '20.35')")
    Else
      Debug "Can't open database !"
    EndIf
  EndProcedure
  
  Procedure UpdateWindow()
    Protected x, y, dx, dy, menu, status
    
    menu = MenuHeight()
    If IsStatusBar(#Status)
      status = StatusBarHeight(#Status)
    Else
      status = 0
    EndIf
    x = 0
    y = 0
    dx = WindowWidth(#Main)
    dy = WindowHeight(#Main) - menu - status
    ResizeGadget(#List, x, y, dx, dy)
  EndProcedure
  
  
  Procedure MyThread(id)
    ;-Test database
    CreateDummyDatabase(0)
    If DatabaseQuery(0, "SELECT * FROM food WHERE weight > 0")
      count = DoShowDatabaseRows(#List, 0, #True)
      DoStatusBarText(#Status, 0, "Items: " + count)
    EndIf
      
  EndProcedure
  
  ; Main
  Procedure Main()
    Protected event, style, dx, dy
    
    style = #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget
    dx = 800
    dy = 600
    
    If OpenWindow(#Main, #PB_Ignore, #PB_Ignore, dx, dy, "Main", style)
      
      ; Menu
      CreateMenu(#Menu, WindowID(#Main))
      MenuTitle("Ablage")
      MenuItem(#MenuStart, "&Start")
      MenuBar()
      MenuItem(#MenuExitApplication, "Be&enden")
      
      ; Enable Fullscreen
      CompilerIf #PB_Compiler_OS = #PB_OS_MacOS 
        Protected NewCollectionBehaviour
        NewCollectionBehaviour = CocoaMessage(0, WindowID(#Main), "collectionBehavior") | $80
        CocoaMessage(0, WindowID(#Main), "setCollectionBehavior:", NewCollectionBehaviour)
        ; Mac default menu
        If Not IsMenu(#Menu)
          CreateMenu(#Menu, WindowID(#Main))
        EndIf
        MenuItem(#PB_Menu_About, "")
        MenuItem(#PB_Menu_Preferences, "")
      CompilerEndIf
      
      ; Gadgets
      ListIconGadget(#List, 0, 0, dx, dy, "recid", 100)
      
      ; Statusbar
      CreateStatusBar(#Status, WindowID(#Main))
      AddStatusBarField(#PB_Ignore)
      
      UpdateWindow()
      
      BindEventGUI()
      ; Main Loop
      Repeat
        event = WaitWindowEvent()
        Select event
          Case #PB_Event_Menu
            Select EventMenu()
                CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
                  
                Case #PB_Menu_About
                  
                Case #PB_Menu_Preferences
                  
                Case #PB_Menu_Quit
                  ExitApplication = #True
                  
                CompilerEndIf
                
              Case #MenuExitApplication
                ExitApplication = #True
                
              Case #MenuStart
                CreateThread(@MyThread(), 0)
                
            EndSelect
            
          Case #PB_Event_Gadget
            Select EventGadget()
              Case #List
                
            EndSelect
            
          Case #PB_Event_SizeWindow
            Select EventWindow()
              Case #Main
                UpdateWindow()
                
            EndSelect
            
          Case #PB_Event_CloseWindow
            Select EventWindow()
              Case #Main
                ExitApplication = #True
                
            EndSelect
            
        EndSelect
        
      Until ExitApplication
      
    EndIf
    
  EndProcedure : Main()
  
  End
  
CompilerEndIf
Zuletzt geändert von mk-soft am 04.07.2017 21:10, insgesamt 1-mal geändert.
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Benutzeravatar
mk-soft
Beiträge: 3695
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Datenbank Threads

Beitrag von mk-soft »

P.S.2

MessageRequester, etc dürfen nicht aus einem Thread aufgerufen werden. Verwende 'DoMessageRequester(...)
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
ccode_new
Beiträge: 1214
Registriert: 27.11.2016 18:13
Wohnort: Erzgebirge

Re: Datenbank Threads

Beitrag von ccode_new »

Danke für den Code.

Ich bekomme es trotzdem noch nicht hin.

Bei mir friert das ganze Fenster immer ein.

Aber ich bleibe dran.

Ich bin mir sicher das irgend wann der Groschen fällt.

Also erst einmal danke!

Ich werde mal weiter probieren.
Betriebssysteme: div. Windows, Linux, Unix - Systeme

no Keyboard, press any key
no mouse, you need a cat
Benutzeravatar
mk-soft
Beiträge: 3695
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Datenbank Threads

Beitrag von mk-soft »

Die Event-Schleife muss im Mainscope natürlich immer laufen... (Nicht im Thread)

Code: Alles auswählen

Repeat
  event = WaitWindowEvent()
  Select event
    Case #PB_Event_CloseWindow
      Break
      
  EndSelect
ForEver
Sonst geht gar nichts...
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
ccode_new
Beiträge: 1214
Registriert: 27.11.2016 18:13
Wohnort: Erzgebirge

Re: Datenbank Threads

Beitrag von ccode_new »

Hi mk-soft,

meine Rückmeldung:

Es funktioniert!!!

Ich hatte nur ein paar kleine Fehler.
Habe meinen Code dabei aber noch etwas optimiert und überarbeitet.

Ich verwende aber nicht dein ThreadToGUI - Modul, weil es:

1. Zu "Oversize" wäre
und
2. Bei mir ohne Anpassung deines Codes einen Zeiger-Error liefert und das Programm einfach abschmiert.
Dieser Fehler würde bei mir an einer Stelle mit PostEvent(...) auftreten.

Ich verwende bei einer Listview einen Callback für die "Kopf-Einträge" (mit Pfeil zum sortieren).

Code: Alles auswählen

ProcedureC LinksklickAufKopfzelle_Rueckruf(ColumnObject.I, Column.I)
  SortColumn = Column
  If SortIsAscending
    PostEvent(#Event_ListIcon_SortDescending)
  Else
    PostEvent(#Event_ListIcon_SortAscending) ;Hierbei kommt sonst der Zeiger-Error bei Verwendung von ThreadToGUI !!!
  EndIf
  
  SortIsAscending ! 1
EndProcedure
Debugger-Ausgabe:

Code: Alles auswählen

 Procedure PostEventCB()
   
    Protected *data.udtParam
   
    *data = EventData()
   
    With *data
      Select \Command ;Hierbei kommt dann bei mir ein Zeigerfehler!!!
        Case #WaitOnSignal
          SignalSemaphore(\Param1)
Mein eigentlich größter Fehler war:
Ich wollte die GUI in Threads stecken, aber ich muss die Datenbank-Dinge in Threads stecken.

Naja egal...
das Thema ist erstmal erledigt.
Betriebssysteme: div. Windows, Linux, Unix - Systeme

no Keyboard, press any key
no mouse, you need a cat
Benutzeravatar
mk-soft
Beiträge: 3695
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Datenbank Threads

Beitrag von mk-soft »

Hast bestimmt die gleiche Event-Nummer wie die vom ThreadToGUI genommen. (#PB_Event_FirstCustomValue)
Bei BindEventGUI(...) kann man die zu verwendete Event-Nummer festlegen. Eine Doppelverwendung für natürlich zu einem Fehler.

Code: Alles auswählen

IncludeFile "Modul_ThreadToGUI.pb"

Enumeration #PB_Event_FirstCustomValue
  #My_Event_ThreadToGUI
  #Event_ListIcon_SortDescending
  #Event_ListIcon_SortAscending
EndEnumeration
...
ThreadToGUI::BindEventGUI(#My_Event_ThreadToGUI)
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Antworten