Showing results 1 to 8 of 8

Thread: Multi-Threading Sub Thread bricht ab

  1. #1

    Default Multi-Threading Sub Thread bricht ab

    Hallo Zusammen,

    ich versuche mich gerade daran Aufgaben in verschiedenen Threads bearbeiten zu lassen, um die Zenon Runtime nicht zu blockieren.

    Hintergrund ist folgender, ich habe eine Funktion die mir einen Report (von einem Energiezähler) als PDF exportiert.
    Da ich nun etwas 400 Zähler habe und nur von bestimmten einen Report möchte (ca.40 bis jetzt Tendenz steigend), dachte ich ein AddIn könnte hilfreich sein.
    Es soll einfach die Verknüpften Variablen ersetzen und die Funktion ausführen.
    Die Idee ist am Anfang eine Variable vom Typ list zu erstellen. Dann müsste ich wenn ein Zähler neu exportiert werden soll den Index der Zählervariablen als neues Element der Liste anlegen.
    Danach führt das Programm die Exportfunktion für jeden Listeneintrag durch.
    Soweit so gut. Das Problem die Export Funktion benötigt ein paar Sekunden um das PDF zu erstellen.
    Das Programm rattert alle Zähler durch, verändert die Variablen und startet die Funktion schneller hintereinander als Sie ausgeführt werden kann.

    Da dachte ich mit Thread.Sleep oder Thread.Task.Delay().Wait etc. könnte nach jedem Schleifendurchlauf ein paar Sekunden warten. Dadurch friert aber die Runtime ein, und die Funktion wird erst nicht ausgeführt.

    So komme ich zum MultiThreading

    Das AddIn soll einen weiteren Thread außerhalb des MainThread erstellen, Sub_Thread_1 hier werden die Variablen gesetzt und gewartet bis die RT Funktion welche dann in einem dritten Thread Sub_Thread.2 gestartet wird fertig ist.

    Die Thread's starten und tun ihre Arbeit auch soweit, aber sobald ich eine Verzögerung in Sub_Thread_1 einbaue Thread.Sleep oder Thread.Task.Delay().Wait oder auch nur eine lange While Schleife, bricht dieser ab. Es kommt lediglich die Fehlermeldung: Der Thread wurde abgebrochen.

    Hier mal ein bisschen Code, mit dem ich das ganze jetzt schon ne weile teste:

    Code:
    
     public class ProjectWizardExtension : IProjectWizardExtension
        {
            #region IProjectWizardExtension implementation
    
    
            public void Run(IProject context, IBehavior behavior)
            {
                try
                {
    
    
                    //###############################################################################################
                    //Liste der Zähler Index, welche exportiert werden sollen, weitere Zähler bitte hier eintragen...
                    List Index = new List();
                    //#0
                    Index.Add("140");
                    //#1
                    Index.Add("325");
                    //#2
                    Index.Add("120");
                    //###############################################################################################
                    Scada.AddIn.Contracts.Function.IFunction obFctTrend;
                    //RT Funktion welche bearbeitet wird
                    obFctTrend = context.FunctionCollection["FU_Export_Report"];
    
    
                    for (int i = 0; i <= Index.Count - 1; i++)
                    {
                        //Ersetzen des Index der verknüpften Variablen in der Funktion auf den Index des aktuellen Zähler
                        //obFctTrend.SetDynamicProperty("Filter[0].DataSet[0].Filter[0].VarInfo[0].Variable", ....
    
    
                    }
                    //RT Funktion in eigenem Thread ausführen
                    Thread thread = new Thread(RunThread_1);
                    thread.Start(context);
                }
                catch (Exception err)
                {
                    context.ChronologicalEventList.AddEventEntry("Fehler: " + err.Message);
                }
            }
            
            #endregion
            private void RunThread_1(Object ob)
            {
                IProject context = (IProject)ob;
                try
                {
                    Thread thread = new Thread(RunThread_2);
                    thread.Start(context);
                    Task.Delay(5000).Wait();
                    context.ChronologicalEventList.AddEventEntry("Sub Thread 1 Ende");
                }
                catch (Exception err)
                {
                    context.ChronologicalEventList.AddEventEntry("Fehler RunThread_1: " + err.Message);
                }
            }
    
    
            private void RunThread_2(Object ob)
            {
                IProject context = (IProject)ob;
                try
                {
                    context.ChronologicalEventList.AddEventEntry("Sub Thread 2 Ende");
                }
                catch (Exception err)
                {
                    context.ChronologicalEventList.AddEventEntry("Fehler RunThread_2: " + err.Message);
                }
            }
        }
    }
    Ist die Lebenszeit des Sub Thread irgendwie begrenzt???
    Ich habe mich mit dem Thema vorher noch nie beschäftigt, aber ich finde hier im Forum sehr wenig dazu, und was ich unter
    github: https://github.com/copa-data gefunden habe, habe ich verwendet um den Code zu erstellen.

    Vielleicht kann mir jemand die richtige Richtung zeigen....

    Gruß Tobias
    Zenon 8.00

    Zenon Server Windows R2016

    Clients WebClientStarter / Windows 10 Enterprise

    ZenonWebserver 8.00



    --trust me i'm an engineer--

  2. #2
    Join Date
    10.12.2007
    Posts
    226

    Default AW: Multi-Threading Sub Thread bricht ab

    Korrigiert mich wenn ich falsch liege: Soweit ich das verstanden habe, kann man zwar ab VSTA und AddIn Multithreading benutzen, der Zugriff auf zenon RT Objekte erfolgt aber immer über die COM-Schnittstelle und damit über den Mainthread der RT. Es bringt also nichts Zugriffe auf mehrere threads zu verteilen, man kann diese threads nur sinnvoll für Aktionen nutzen, die nicht auf zenon Objekte zugreifen (also in deinem Fall z.B.: wenn du die Werte aller Variablen zuerst sammelst und dann diese Werte an eine Funktion übergibst die in einem neuen thread eine von zenon unabhängige Druckfunktion benutzt)

    Außerdem: Ich kann nicht erkennen, wo du die zuerst manipulierte RTFunction eigentlich starten willst.

    Alternativvorschlag: Benutz vielleicht einen FileSystemWatcher, der könnte das Exportverzeichnis überwachen und sobald die erste Pdf da ist, die nächste aus der Liste exportieren.

  3. #3
    Join Date
    02.02.2012
    Posts
    15

    Default Aw: Multi-Threading Sub Thread bricht ab

    Hallo Tobias,

    ich glaube, du solltest hier einen Runtime Service, statt eines Wizards verwenden. Die Wizards im Add-Framework werden in einer eigenen App-Domain ausgeführt. Diese App-Domain wird geladen, wenn der Wizard gestartet – also die Run()-Methode aufgerufen wird – und wenn die Run()-Methode abgeschlossen wird, die App-Domain wieder entladen. Das bedeutet auch, alle Threads, die zu dieser App-Domain gehören, werden gestoppt. Dieses Verhalten ist von der .NET Laufzeitumgebung so vorgegeben.

    Mir ist auch nicht klar, was genau in einen eigenen Thread ausgelagert werden soll. Das Aufrufen des zenon Funktion macht da wenig Sinn, denn die zenon Funktion wird von der Runtime ausgeführt. In welchem Thread diese Funktion dann ausgeführt wird, kann über die API nicht beeinflusst werden. Man teilt der Runtime über die API also nur mit, dass eine Funktion ausgeführt werden soll. Um alles weitere kümmert sich die Runtime selbst.

    Im Übrigen hat mst Recht, auch das Add-In Framework benutzt die COM-Schnittstelle von zenon. Diese ist als Single Threaded Appartment implementiert. Der Zugriff auf die zenon Objekte erfolgt daher immer im Mainthread.

    mfG Martin

  4. #4

    Default AW: Multi-Threading Sub Thread bricht ab

    Hallo mst,

    vielen Dank für die Antwort, das habe ich leider nicht gewusst.

    Die RT Funktion sollte im Thread_2 gestartet werden. Da ich hier viel am Probieren war, steht die Code Zeile so jetzt aber garnicht mehr drin.
    Mir ging es in dem Beispiel hier darum, dass ich mein erdachtes Konzept nur mal Teste (mittels der Einträge in die CEL) wollte ich kontrollieren ob die Thread`s abgearbeitet werden und die Runtime bedienbar bleibt.
    Danke für den Hinweis auf SystemFileWatcher, das werde ich mir mal anschauen.
    Warum in meinem Beispiel jetzt aber immer die Fehlermeldung "Thread Abgebrochen" kommt, wäre auch noch interessant.

    Gruß Tobias
    Last edited by zero : 15th June 2020 at 11:23
    Zenon 8.00

    Zenon Server Windows R2016

    Clients WebClientStarter / Windows 10 Enterprise

    ZenonWebserver 8.00



    --trust me i'm an engineer--

  5. #5

    Default AW: Multi-Threading Sub Thread bricht ab

    Hallo Martin,

    danke für die Info, das beantwortet warum die Threads abbrechen.
    Das Warten bis zum erneuten Ausführen der RT Funktion sollte in einem eigenen Thread laufen.

    Ich werde mal versuchen das ganze um zu stricken, unter Berücksichtigung der neuen Erkenntnisse
    Vielen Dank.

    Gruß Tobias
    Last edited by zero : 15th June 2020 at 11:25
    Zenon 8.00

    Zenon Server Windows R2016

    Clients WebClientStarter / Windows 10 Enterprise

    ZenonWebserver 8.00



    --trust me i'm an engineer--

  6. #6

    Default AW: Multi-Threading Sub Thread bricht ab

    Vielen Dank nochmal für das Feedback.
    Ich habe es jetzt hinbekommen,
    als Service habe ich jetzt einen "Zwischen Threat" der immer einige Sekunden wartet bis die Funktion ein weiteres Mal ausgeführt wird, ohne dass die Runtime blockiert wird.
    Die separaten Threads habe ich in eine separate Klasse gepackt, ob das zwingen nötig ist, habe ich jetzt nicht getestet. So klappt es auf jeden Fall
    Nur die Auslösung ist noch ein bisschen unschön, ich habe eine Zeitfunktion die eine Dummy Funktion auslöst, die eigentlich nichts macht, nur einen sinnlosen Sollwert toggelt.
    Wäre schöner, ich könnte das direkt mit der Zeitfunktion antriggern...

    Code:
    
    
    using System;
    using Scada.AddIn.Contracts;
    using System.Threading;
    using System.Collections.Generic;
    using System.Windows.Forms;
    
    
    namespace Export_Reports
    {
        /// 
        /// Description of Project Service Extension.
        /// 
        [AddInExtension("Export Reports als Dienst", "Dieser Dienst führt gesteuert durch eine Zeitfunktion Exporte von Energie Reports durch", DefaultStartMode = DefaultStartupModes.Auto)]
        public class ProjectServiceExtension : IProjectServiceExtension
        {
            #region IProjectServiceExtension implementation
            Scada.AddIn.Contracts.IProject myProject;
            public void Start(IProject context, IBehavior behavior)
            {
                //Errorhandling
                try
                {
                    //Deklaration
                    myProject = context;
                    //Anmelden des Event zur Überwachung der Dummyfunktion welche von der Zeitfunktion ausgelöst wird
                    myProject.FunctionCollection.FunctionExecuting += FunctionCollection_FunctionExecuting;
                }
                //Errorhandling
                catch (Exception)
                {
                    return;
                }
            }
            public void FunctionCollection_FunctionExecuting(object sender, Scada.AddIn.Contracts.Function.FunctionExecutingEventArgs e)
            {
                //Errorhandling
                try
                { 
               
                    //Deklaration
                    List Index = new List();
                    //Instandzieren der Klasse + übergabe Parameter an Constructor
                    ThreadContainer myThread = new ThreadContainer(Index, myProject);
    
    
                    //Wenn ausgeführte Funktion gesuchte Dummyfunktion ist, starte mit dem Export
                    if (e.Function.Name == "FU_ADDIN_Export_Report")
                    {
                            //###############################################################################################
                            //Liste der ZählerIndexe, welche exportiert werden sollen, weitere Zähler bitte hier eintragen...
                            //Druckluft Zähler #0
                            Index.Add("140");
                            //Strom Zähler  #1
                            Index.Add("325");
                            //Strom Zähler  #2
                            Index.Add("120");
                            //###############################################################################################
                            //Starte seperaten "zwischen" Thread der keinen Zugriff auf API hat 
                            Thread Thread_1 = new Thread(myThread.Thread_1);
                            Thread_1.Start();
                    }
                }
                //Errorhandling
                catch (Exception)
                {
                    return;
                }
            }
                public void Stop()
            {
                //Abmelden des Event zur Überwachung der Funktion
                myProject.FunctionCollection.FunctionExecuting -= FunctionCollection_FunctionExecuting;
            }
    
    
            //Seperate Klasse für Sub Threads
            public class ThreadContainer
            {
                //Deklaration
                List m_Index;
                IProject m_Project;
    
    
                //Constructor (kann keinen Rückgabewert enthalten)
                public ThreadContainer(List Index, IProject myProject)
                {
                    m_Index = Index;
                    m_Project = myProject;
                }
    
    
                //zwischen Threat
                //Dient zur Verzögerung zwischen den einzelnen Funktionsaufrufen
                //Bis ein pdf erzeugt ist und die Funktion erneut verwedet werden kann,
                //dauert es bis zu 3 Sekunden
                public void Thread_1()
                {
                    //Wichtig keine Aufrufe der API damit Thread ausßerhalb des Main Thread läuft und die Runtime nicht blockiert wird.
                    //Errorhandling
                    try
                    {
                        for (int i = 0; i <= m_Index.Count - 1; i++)
                        {
                            //Aufruf Thread zum Ändern und Ausführen der Reportfunktionen
                            Thread thread_2 = new Thread(Thread_2);
                            //Übergabe des jeweiligen Index an Thread_2
                            thread_2.Start(m_Index[i]);
                            //Verzögerung 5sek
                            Thread.Sleep(10000);
                        }  
                    }
                    //Errorhandling
                    catch (Exception)
                    {
                        return;
                    }
                }
    
    
                public void Thread_2(Object ob)
                {
                    //Errorhandling
                    try
                    { 
                        //Deklaration
                        Scada.AddIn.Contracts.Function.IFunction obFctTrend;
                        int i = Convert.ToInt32((string) ob);
    
    
                        //RT Funktion welche bearbeitet wird (Wochenreport)
                        obFctTrend = m_Project.FunctionCollection["FU_Export_Report_Woche"];
                        //Ersetzen des Index der verknüpften Variablen in der Funktion auf den Index des aktuellen Zähler
                        obFctTrend.SetDynamicProperty("Filter[0].DataSet[0].Filter[0].VarInfo[0].Variable", m_Project.VariableCollection["Visu_UDINT_EnergieVerbrauch[" + i + "]"].Name);
                        obFctTrend.SetDynamicProperty("Filter[0].DataSet[0].Filter[0].VarInfo[0].VarName", m_Project.VariableCollection["Visu_UDINT_EnergieVerbrauch[" + i + "]"].Name);
                        obFctTrend.SetDynamicProperty("Filter[0].DataSet[1].Filter[0].VarInfo[0].Title", m_Project.VariableCollection["EnergyCounter[" + i + "].sFunctionName"].Name);
                        obFctTrend.SetDynamicProperty("Filter[0].DataSet[1].Filter[0].VarInfo[1].Title", m_Project.VariableCollection["EnergyCounter[" + i + "].sFunctionDescription"].Name);
                        obFctTrend.SetDynamicProperty("Filter[0].DataSet[2].Filter[0].VarInfo[0].Title", m_Project.VariableCollection["EnengyCounter[" + i + "].Monatswechsel"].Name);
                        obFctTrend.SetDynamicProperty("Filter[0].DataSet[2].Filter[0].VarInfo[1].Title", m_Project.VariableCollection["EnergyCounter[" + i + "].Vormonatswechsel"].Name);
                        obFctTrend.SetDynamicProperty("Filter[0].DataSet[3].Filter[0].VarInfo[0].Title", m_Project.VariableCollection["EnergyCounter[" + i + "].nType"].Name);
                        obFctTrend.SetDynamicProperty("Filter[0].DataSet[3].Filter[0].VarInfo[1].Title", m_Project.VariableCollection["EnergyCounter[" + i + "].nUnit"].Name);
                        obFctTrend.CreateDynamicProperty("Filter[0].Fct_Filename");
                        obFctTrend.SetDynamicProperty("Filter[0].Fct_Filename", "D:\\Export Runtime\\Export_" + i + "_Woche");
                        
                        //Ausführen der geänderten RT Funktiuon
                        obFctTrend.Execute();
    
    
                        //Meldung in CEL schreiben
                        m_Project.ChronologicalEventList.AddEventEntry("Report für " + m_Project.VariableCollection["EnergyCounter[" + i + "].sFunctionName"].Name + " erzeugt");
                    }
                    //Errorhandling
                    catch (Exception err)
                    {
                        //Fehlermeldung in CEL schreiben
                        m_Project.ChronologicalEventList.AddEventEntry("Fehler in Thread 2 bei Export von Reports: " + err.Message);
                    }
                }
            }
            #endregion
        }
    }
    Zenon 8.00

    Zenon Server Windows R2016

    Clients WebClientStarter / Windows 10 Enterprise

    ZenonWebserver 8.00



    --trust me i'm an engineer--

  7. #7
    Join Date
    10.12.2007
    Posts
    226

    Default AW: Multi-Threading Sub Thread bricht ab

    Hallo zero,
    schön dass Du eine Lösung gefunden hast... auch wenn ich ein bisschen verwundert bin dass sie funktioniert. Aber anscheinend klappt das mit diesem Zwischenthread.
    Auf die Gefahr hin genau das Gegenteil vom Profi martin_f zu behaupten, würde ich dir aber zweierlei empfehlen:
    1. Die List und die ThreadContainer Klasse würde ich auf alle Fälle erst nach der Abfrage der Funktion erzeugen. Als automatisch gestarteter Service läuft das AddIn während der ganzen Runtime und JEDE ausgeführte Funktion löst ja das FunctionExecuting Event aus. Das dürfte den GarbageCollector auf die Dauer ganz schön beschäftigen.
    2. Lässt sich das ganze nicht auch als ProjectWizard umsetzen? Dann könntest du das mit der Zeitfunktion direkt ausführen und Punkt 1 wäre auch erledigt.

  8. #8

    Default AW: Multi-Threading Sub Thread bricht ab

    Hi, 

    zu Punkt 1
    vielen Dank für den Tipp. Das werde ich so machen Meine Programmierkenntnisse  bestehen leider nur aus dem Prinzip Learning by doing, daher fehlt mir oft der Überblick

    zu Punkt 2 
    ursprünglich war es als Wizard programmiert (erstes Beispiel).
    Hier besteht das von martin_f beschriebene Problem, dass der MainThread ja schneller beendet wird als meine SubThreads, welche ja relativ lange beschäftigt sind.
    Wird der MainThread beendet, wird auch die App-Domian 1 bis 2 Sekunden später beendet und somit alle SubThreads zwangsweise auch.

    Gruß Tobias
    Zenon 8.00

    Zenon Server Windows R2016

    Clients WebClientStarter / Windows 10 Enterprise

    ZenonWebserver 8.00



    --trust me i'm an engineer--

Similar Threads

  1. Verschwundener Thread
    By zero in forum Forum Issues
    Replies: 2
    Last Post: 13th February 2020, 13:09
  2. Darstellung ETM bricht am Client ab
    By drehpet in forum Webserver/Webclient
    Replies: 3
    Last Post: 30th August 2016, 13:09
  3. Start a thread in VBA
    By mrk in forum VBA
    Replies: 1
    Last Post: 19th March 2008, 10:12

Tags for this Thread

Posting Rules

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •