GUI- und WorkerThread

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.

GUI- und WorkerThread

Beitragvon joggel » 20.07.2017, 09:08

Huhu,

ich überarbeite eine Anwendung. Ich möchte jetzt, dass das GUI vollkommen losgelöst von meiner Logik existiert.
Ich arbeite mit C# und Winforms. Aber das ist auch erstmal egal.

Ich habe mir dazu folgendes überlegt:
Etwas Code, da Code meist mehr sagt als 1000 Worte <3
Code: Ansicht erweitern :: Alles auswählen

   public partial class MainForm : Form
    {
        // in diesem Thread findet die ganze arbeit statt. der HauptThread wird nur für das darstellen und die
        // Interaktion mit dem GUI verwendet
        System.Threading.Thread logicThread;

        listOfLogicActions List<ActionBase> = new List<ActionBase>();
               
        public MainForm()
        {
            InitializeComponent();

            logicThread = new System.Threading.Thread(DoThisAllTheTime);
            logicThread.Start();

        }

        private void ButtonOpenProtocol_Click(object sender, EventArgs e)
        {
            // Displays an OpenFileDialog so the user can select a data file.
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Multiselect = false;
            openFileDialog.Filter = "blaBla";

            if ((openFileDialog.ShowDialog() == DialogResult.OK) &&
                (openFileDialog.CheckFileExists == true))
            {
                               
                                listOfLogicActions.Add(new ActionOpenFile(openFileDialog.FileName));
            }
        }



        public void DoThisAllTheTime()
        {
              while(true)
              {
                for(int index=0; index<listOfLogicActions.Count; ++index)
                {
                        listOfLogicActions[index].doIt();
                }
              }
        }
    }
 


So mein erster Gedanke.
Jetzt rattert der logicWorkerThread aber die GANZE ZEIT...

Meine Frage:
Kann ich das irgendwie anders lösen, dass nicht die ganze Zeit eine Schleife durchratter, sondern das das Durchlaufen der listOfLogicActions nur angestoßen wird, wenn ein neuer Eintrag vorhanden ist?
Zuletzt geändert von joggel am 20.07.2017, 13:39, insgesamt 1-mal geändert.
...
Benutzeravatar
joggel
Establishment
 
Beiträge: 1178
Registriert: 06.11.2007, 19:06
Wohnort: Dresden

Re: GUI- und WorkerThread

Beitragvon Krishty » 20.07.2017, 09:40

Normalerweise richtet man sich ein Event ein, auf das der Arbeiter wartet. Nachdem etwas Neues in der Schleife ist, wird das Event ausgelöst (wird signalled) und der Arbeiter hört auf zu warten und geht an die Arbeit. Wenn keine Arbeit mehr da ist, wartet er wieder auf das Event.

Konkret in deinem Fall kann ich mir aber vorstellen, dass C# schon irgendwelche Listenklassen hat, die von Haus aus waitable sind oder so.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6040
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: GUI- und WorkerThread

Beitragvon joggel » 20.07.2017, 09:46

Danke Krishty, das mit dem Event war ein guter Hinweis.
Ein Tutorial
...
Benutzeravatar
joggel
Establishment
 
Beiträge: 1178
Registriert: 06.11.2007, 19:06
Wohnort: Dresden

Re: GUI- und WorkerThread

Beitragvon joggel » 20.07.2017, 10:15

Nein, irgendwie ist das auch sinnlos für mich!
Die aktuelle Arbeit wird ja auch bei einem Change-Event unterbrochen ==> und das möchte ich ja nicht!!

Oder ist das überhaupt sinnvoll was ich vorhabe :?
...
Benutzeravatar
joggel
Establishment
 
Beiträge: 1178
Registriert: 06.11.2007, 19:06
Wohnort: Dresden

Re: GUI- und WorkerThread

Beitragvon BeRsErKeR » 20.07.2017, 15:43

Warum startest du denn nicht einfach den Thread (oder besser einen BackgroundWorker), wenn du eine spezifische Aktion ausführen möchtest? Ansonsten würde ich, wie Krishty schon sagte, auf ein Signal warten und nicht permanent einen Loop durchlaufen lassen.

Schau dir dazu am besten mal die WaitHandle-Klasse an. Da gibt es auch ein gutes Beispiel mit AutoResetEvents. Zusätzlich kannst du auch noch ein ManualResetEvent einbauen, was du z.B. beim Shutdown deiner Anwendung signalst. Damit der Thread auch sauber beendet, wenn du die Anwendung schließt.

Du kannst z.B. mit WaitAny auf 1 AutoResetEvent und 1 ManualResetEvent warten. Das erste signalst du, wenn du die Liste abarbeiten möchtest und das andere wenn die Anwendung schließt. Das WaitAny wird dann die Blockierung aufheben, wenn ein Signal von beiden auslöst. Für den 2. Fall (Anwendung schließt) musst du vor der inneren For-Schleife halt nochmal auf ein Shutdown-Flag oder ähnliches prüfen und entsprechend rausspringen. Das WaitHandle.WaitAny platzierst du z.B. innerhalb der Endlosschleife.
Ohne Input kein Output.
Benutzeravatar
BeRsErKeR
Establishment
 
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: GUI- und WorkerThread

Beitragvon joggel » 20.07.2017, 15:53

BeRsErKeR hat geschrieben:Warum startest du denn nicht einfach den Thread (oder besser einen BackgroundWorker), wenn du eine spezifische Aktion ausführen möchtest? Ansonsten würde ich, wie Krishty schon sagte, auf ein Signal warten und nicht permanent einen Loop durchlaufen lassen.

So wie ich das von Krishty verstanden habe, ist das soetwas wie ein Event.
Also die Liste merkt, dass ihr inhalt verändert wurde, und reagiert promt! Und da wird ja die vorherige/aktuelle Arbeit unterbrochen => und das wollte ich eigentlich vermeiden.

Bsp:
Ich drücke den "Öffne eine Datei"-Button, dieser zeigt mir ein Openfiledialog, mit dem ich eine Datei auswähle. Schluss!!! AUS!! Mehr möchte ich durch diese Aktion nicht tun!
Dann soll auch diese Funktion, die beim Klick auf den "Öffne eine Datei"-Button ausgeführt wird, auch wieder beendet werden.
Erst danach soll etwas mit der Datei geschehen, die ich ausgewählt habe.

Und wenn ich noch in dieser Funktion (Klick auf "Öffne Datei"-Button) einen Thread starte, gefällt mir das auch nicht so sehr.

Ich hätte gerne irgend etwas globaleres...
Bei Qt läuft das doch auch so ab, das die Signale erst in eine Liste gepackt werden, und dann zu einem späteren Zeitpunkt abgearbeitet werden....oder?

Vlt ist das auch totaler Unsinn was ich mir überlege.
Mein Ziel ist es eben, die Logik so weit wie möglich von der GUI-Interaktion zu lösen...
...
Benutzeravatar
joggel
Establishment
 
Beiträge: 1178
Registriert: 06.11.2007, 19:06
Wohnort: Dresden

Re: GUI- und WorkerThread

Beitragvon BeRsErKeR » 20.07.2017, 16:08

Du kannst ja deinen "globalen" Thread so lassen. Aber anstatt, dass du permanent die Endlosschleife durchlaufen lässt, wartest du am Anfang der Endlosschleife auf ein Signal. Das Signal setzt du immer dann, wenn du eine Aktion für die Liste durchführen möchtest. Der GUI-Thread setzt also nur das Signal und läuft fröhlich weiter. Der Logik-Thread macht ohne Signal nichts außer Warten. Wenn ein Signal kommt, arbeitet er die Listenoperationen ab und wartet auf das nächste Signal. Der Charme am AutoResetEvent ist es, dass das Signal wieder automatisch auf den AUS-Zustand wechselt, sofern ein Wait-Aufruf das Signal verarbeitet hat.

Das zusätzliche Event beim Shutdown würde ich einfach einbauen, damit der Thread auch irgendwann mal beendet werden kann. Weil anders kannst du das Wait schlecht abbrechen. In deiner Version mit der Endlosschleife hat der Thread ja bislang auch wenig Chancen mal das Zeitliche zu segnen.
Ohne Input kein Output.
Benutzeravatar
BeRsErKeR
Establishment
 
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: GUI- und WorkerThread

Beitragvon BeRsErKeR » 20.07.2017, 16:15

Ich hab hier leider gerade nichts zum Programmieren. Daher hier ungetestet:

Code: Ansicht erweitern :: Alles auswählen
public partial class MainForm : Form
{
    // in diesem Thread findet die ganze arbeit statt. der HauptThread wird nur für das darstellen und die
    // Interaktion mit dem GUI verwendet
    System.Threading.Thread logicThread;

    List<ActionBase> listOfLogicActions = new List<ActionBase>();
    AutoResetEvent listActionEvent = new AutoResetEvent(false);
    ManualResetEvent shutdownEvent = new ManualResetEvent(false);
    bool shutdown = false;
               
    public MainForm()
    {
        InitializeComponent();

        logicThread = new System.Threading.Thread(DoThisAllTheTime);
        logicThread.Start();
    }

    private void MainForm_Closed(object sender, EventArgs e)
    {
        shutdown = true;
        shutdownEvent.Set();
    }

    private void ButtonOpenProtocol_Click(object sender, EventArgs e)
    {
        // Displays an OpenFileDialog so the user can select a data file.
        OpenFileDialog openFileDialog = new OpenFileDialog();
        openFileDialog.Multiselect = false;
        openFileDialog.Filter = "blaBla";

        if ((openFileDialog.ShowDialog() == DialogResult.OK) &&
            (openFileDialog.CheckFileExists == true))
        {
            listOfLogicActions.Add(new ActionOpenFile(openFileDialog.FileName));
            listActionEvent.Set(); // Listenabarbeitung signalisieren
        }
    }

    public void DoThisAllTheTime()
    {
        while(true)
        {
            WaitHandle.WaitAny(new WaitHandle[] { listActionEvent, shutdownEvent });

            if (shutdown)
                return;
         
            for(int index=0; index<listOfLogicActions.Count; ++index)
            {
                listOfLogicActions[index].doIt();
            }
        }
    }
}
Ohne Input kein Output.
Benutzeravatar
BeRsErKeR
Establishment
 
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: GUI- und WorkerThread

Beitragvon Krishty » 20.07.2017, 16:27

joggel hat geschrieben:Bsp:
Ich drücke den "Öffne eine Datei"-Button, dieser zeigt mir ein Openfiledialog, mit dem ich eine Datei auswähle. Schluss!!! AUS!! Mehr möchte ich durch diese Aktion nicht tun!
Dann soll auch diese Funktion, die beim Klick auf den "Öffne eine Datei"-Button ausgeführt wird, auch wieder beendet werden.
Erst danach soll etwas mit der Datei geschehen, die ich ausgewählt habe.
Klingt nach einem Promise oder einem Future. (Ganz ab davon, dass solche Dialoge im selben Thread laufen können, so lange sie modal sind!)

Statt selber Threads zu starten, würde ich auch den Thread Pool nutzen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6040
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: GUI- und WorkerThread

Beitragvon joggel » 20.07.2017, 16:34

@Krishty
Naja...ist meine Anforderung. Will das "ordentlich" machen.

@Berserker
Danke. Werd mir das mal anschauen.
Aber läuft da in dem extra Thread nicht auch die ganze Zeit die while-Schleife?
...
Benutzeravatar
joggel
Establishment
 
Beiträge: 1178
Registriert: 06.11.2007, 19:06
Wohnort: Dresden

Re: GUI- und WorkerThread

Beitragvon BeRsErKeR » 20.07.2017, 16:37

joggel hat geschrieben:@Berserker
Danke. Werd mir das mal anschauen.
Aber läuft da in dem extra Thread nicht auch die ganze Zeit die while-Schleife?


Nein. Es ist natürlich nach wie vor eine Endlosschleife, aber der Thread wartet beim WaitAny bis ein Signal kommt und somit läuft da erstmal nichts weiter.
Ohne Input kein Output.
Benutzeravatar
BeRsErKeR
Establishment
 
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: GUI- und WorkerThread

Beitragvon Krishty » 20.07.2017, 16:58

Krishty hat geschrieben:Klingt nach einem Promise oder einem Future.
joggel hat geschrieben:Naja...ist meine Anforderung. Will das "ordentlich" machen.

Promise und Future sind Programmierkonzepte. Dein Datei-Loader bekommt z.B. von der UI einen Pfad versprochen, und kann abwarten, dass die UI dieses Versprechen asynchron einlöst ;)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6040
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: GUI- und WorkerThread

Beitragvon BeRsErKeR » 20.07.2017, 17:10

Krishty hat geschrieben:
Krishty hat geschrieben:Klingt nach einem Promise oder einem Future.
joggel hat geschrieben:Naja...ist meine Anforderung. Will das "ordentlich" machen.

Promise und Future sind Programmierkonzepte. Dein Datei-Loader bekommt z.B. von der UI einen Pfad versprochen, und kann abwarten, dass die UI dieses Versprechen asynchron einlöst ;)


Im Prinzip ist das Wait/WaitAny oben eine Art der Umsetzung des Future in C#. Man kann sowas natürlich auch anders lösen, z.B. mit IAsyncResult und Co. Aber für diesen einfachen Anwendungsfall geht imho auch ein Parallelthread + Wait + ActionList.
Ohne Input kein Output.
Benutzeravatar
BeRsErKeR
Establishment
 
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: GUI- und WorkerThread

Beitragvon MasterQ32 » 20.07.2017, 17:29

Ich poste hier einfach mal das hier, da es scheint, als würde es joggels anforderungen entsprechen:
https://docs.microsoft.com/en-us/dotnet/csharp/async
Duct tape is like the force. It has a light side, a dark side, and it holds the world together.
Benutzeravatar
MasterQ32
Felix Queißner
Establishment
 
Beiträge: 958
Registriert: 07.10.2012, 14:56

Re: GUI- und WorkerThread

Beitragvon Krishty » 20.07.2017, 17:31

BeRsErKeR hat geschrieben:Im Prinzip ist das Wait/WaitAny oben eine Art der Umsetzung des Future in C#. Man kann sowas natürlich auch anders lösen, z.B. mit IAsyncResult und Co. Aber für diesen einfachen Anwendungsfall geht imho auch ein Parallelthread + Wait + ActionList.
Bezog sich auch nicht auf deine Antwort; ich will nur, dass joggel einen Überblick darüber bekommt, was er da tut :)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6040
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Nächste

Zurück zu Programmiersprachen, Quelltext und Bibliotheken

Wer ist online?

Mitglieder in diesem Forum: Exabot [Bot] und 3 Gäste