Laden...

Gibt es die Möglichkeit den Ui Thread als MTA laufen zu lassen?

Erstellt von D1990 vor 5 Jahren Letzter Beitrag vor 5 Jahren 2.985 Views
D
D1990 Themenstarter:in
8 Beiträge seit 2019
vor 5 Jahren
Gibt es die Möglichkeit den Ui Thread als MTA laufen zu lassen?

Hallo Zusammen !

Ich lese schon seit längerer Zeit mal in diesem Forum nach wenn ich Probleme in der C# Programmierung habe, leider bin ich diesmal auf ein Problem gestoßen das ich durch das Nutzen von Suchfunktionen und Google nicht lösen konnte.

Derzeit arbeite ich an einem Projekt mit der Siemens API TIA Openness. Dabei schreibe ich an einer WPF- Anwendung welche ich nach dem MVVM Design Pattern strukturiert habe.

Im MainViewModel meines Codes wird anhand eines OpenFileDialogs eine Projektdatei gesucht welche dann ein Objekt des Typs TiaPortal instantiiert. Dies geschieht ganz normal im GUI-Thread.
Im weitereren Verlauf des Programms nutze ich dann einen async Task der das beschriebene Objekt aus eine anderen Thread bearbeitet damit während der langen Laufzeit die GUI nicht einfriert. Wenn währendessen Änderungen an GUI Elementen durchgeführt werden mussten, löste ich dies mittels der Dispatcher.Invoke Methode.

Dies funktionierte gut bis eine neue Version der .DLL veröffentlicht wurde:> Fehlermeldung:

Exception : Cross-thread operation is not valid in Openness within STA.

Nach einer langen Suche über STA und MTA im Kontext WPF kam ich dann
zum Entschluss mich in diesem Forum anzumelden um diesen Beitrag zu schreiben:

Gibt es die Möglichkeit den Ui Thread als MTA laufen zu lassen, bzw einen Workaround?
Leider kam ich noch zu keinem vernünfigten Ergebniss.

In der .DLL Dokumentation fand ich folgenden Text:

Thread-übergreifender Betrieb
Der Zugriff auf Openness-Objekte ist nicht inhärent threadsicher.
Wenn Sie Multithreading zur Verbesserung der Performance Ihrer Openness-Anwendung
einsetzen, wird die Erstellung der Instanz des TIA Portals mit MTA empfohlen.
Wenn TIA Portal innerhalb eines STA-Thread erstellt oder angehängt wird, sollte auf alle
Openness-Objekte in Zusammenhang mit dieser Instanz des TIA Portals aus demselben STAThread
zugegriffen werden; andernfalls wird eine Ausnahme ausgelöst.

Selbstverständlich könnte ich jetzt wieder die Bearbeitung im Main-thread(STA) durchführen was aber gleichzeitg das Einfrieren meiner Anwendung bedeuten würde.

Da ich ein kompletter Newbie im Bereich des Multithreadings bin bitte ich euch Tipps bzw. Anregungen wie ich das beschriebene Problem lösen könnte.

Beste Grüße und vielen Dank im Vorraus !

W
955 Beiträge seit 2010
vor 5 Jahren

Hallo,
fehlerhafte threadübergreifende Vorgänge erzeugen mitunter ein undefiniertes Verhalten, d.h. es wird nicht immer erkannt dass ein unzulässiger Zugriff durchgeführt wird. Es könnte also auch sein dass Du das schon immer fehlerhaft aufgerufen hast und die neue LibVersion das jetzt aber bemerkt und anmeckert.

D
D1990 Themenstarter:in
8 Beiträge seit 2019
vor 5 Jahren

Vielen Dank für deine Antwort Witte,

Leider finde ich nur Informationen die mir zeigen wie ich auf ein Control das dem UI-Thread gehört zugreifen kann, aber nicht wie auf ein Objekt.
Ich habe mir heute zum zweiten mal ein Kapitel zum Thema Threading(Rheinwerk Openbooks) durchgelesen und komme immernoch nicht weiter.

Ich teile deine Vermutung das schon vorher etwas falsch war, was nahe liegt da ich ein Anfänger in der ganzen C# Materie bin. Deshlab mal vereinfacht mein Code:



public class myclass
{
     public myclass()
     {

      object= null;


     }
     private APIObject _object;
     public APIObject object(get.....

     private void mycommand_executed(object sender, EventArgs e)
     {

        if(object==null)
           {
            object = new APIObject();         
           }
      }

    private aysnc void secondcommand_executed(object sender, EventArgs e)
   {
    //Hier passiert anderes zeug

   await Task.Run(() => Configuration);
  
   }

    private async Task Configuration()
   {
            if(object==null)
           {
            object = new APIObject() ;        
           }
     //Hier kommen laufzeitintensive Methodenaufrufe des API Objekts
           object.DoSomething();
   }

}


Im Funktionsablauf meines Programms kann es also vorkommen das der Konstruktor des Objektes schon vor dem Starten des Task ausgeführt wird und somit das Objekt dem UI-Thread gehört (Wenn ich das richtig verstanden habe).

Ich hoffe das mir jemand weiterhelfen kann und das ganze nur ein blöder Anfängerfehler ist 😃

Viele Grüße und Danke

16.806 Beiträge seit 2008
vor 5 Jahren

Ich kann mir nicht vorstellen, dass in dem Buch wirklich mit dem Typ object gearbeitet wird - also quasi untypisiert.

Vermutlich stimmt die gesamte Struktur nicht, denn man greift i.d.R. nicht auf irgendein Control zu - man bindet Informationen.
Sieht für mich jetzt nicht so aus, dass die Basis der Struktur korrekt ist.

Leider finde ich nur Informationen dei mir zeigen wie ich auf ein Control das dem UI-Thread gehört zugreifen kann, aber nicht wie auf ein Objekt.

Man greift auch nicht auf ein "Objekt" zu.
Das Objekt selbst liefert Events, auf die man lauscht - nicht umgekehrt.

D
D1990 Themenstarter:in
8 Beiträge seit 2019
vor 5 Jahren

Hallo Abt !

Bitte verzeih mir, selbstverständlich habe ich nicht mit dem Typ

object

gearbeit. Sollte nur ein Platzhalter für einen bestimmten Typ sein um es alllgemein zu halten. Ich gebe zu dass das nicht gerade geschickt gewählt war 😃.

Bitte übergeh auch meine Äußerung zu den Controlls, da es nicht den Kernpunkt meiner Frage darstellt.

Beste Grüße !

16.806 Beiträge seit 2008
vor 5 Jahren

Es ist keine besonders gute Idee Code zu zeigen, den Du verfälscht hast, um es "allgemein" zu halten... das bringt oft eher Probleme, wie in diesem Fall.
Zeig den wahren Code. Zur Not nenn die Klassen um, aber verfälsche es bitte nicht.

D
D1990 Themenstarter:in
8 Beiträge seit 2019
vor 5 Jahren

Ich hoffe das es so besser zu verstehen ist. Die Exception kommt dann wenn vor
dem StartConfigurationCommand der OpenTIACommand genutzt wurde.



namespace TIA.ViewModels
{
    class MainWindowViewModel : ViewModelBaseClass
    {
        public MainWindowViewModel()
        {		
			_Tia = null;
			_Project=null;
			_Projects=null;
            StartConfigurationCommand = new  CommandBase(StartConfigurationCommand_Executed);
            OpenTiaCommand = new CommandBase(OpenTiaCommand_Executed);			
		}
	
		private  TiaPortal _Tia;
		private  TiaProject _Project;
		private ProjectComposition _Projects;	
		public CommandBase StartConfigurationCommand { get; set; }
		public CommandBase OpenTiaCommand { get; set; }
		
		private void OpenTiaCommand_Executed(object sender, EventArgs e) 
		{
			if(_Tia==null)
			{
				_Tia= new TiaPortal();
				_Projects=_Tia.Projects;
				_Project=_Projects.Open("Pfad");
			}
		}
		
		
		private async void StartConfigurationCommand_Executed(object sender, EventArgs e)        
        {
			try
			{
				await Task.Run(() => Configuration());  
			}
			catch (Exception u)
			{
				System.Windows.MessageBox.Show(u.Message, "Error");
			}
			
		}
		
		private async Task Configuration()
		{
			if(_Tia==null)
			{
				_Tia= new TiaPortal();
				_Projects=_Tia.Projects;
				_Project=_Projects.Open("Pfad");
			}
			//HIER ENTSTEHT DIE EXCEPTION
			ProjectLibrary _projectLibrary = _Project.ProjectLibrary;
			
			
		}
				
	}	
}

W
955 Beiträge seit 2010
vor 5 Jahren

Die Exception kommt dann wenn vor
dem StartConfigurationCommand der OpenTIACommand genutzt wurde. Überlappen sich beide zeitlich? Kommt der Fehler auch wenn die Befehle synchron ausgeführt werden? Möglicherweise greifen beide Methoden auf dieselben Objekte zu und müssen voneinander geschützt werden (SemaphoreSlim o.ä.)

D
D1990 Themenstarter:in
8 Beiträge seit 2019
vor 5 Jahren

Die Commands sollten sich nicht überlappen, da ich die Nutzung der Commands (CanExecute) je nach Situation unterbinde.
Deine zweite Frage verstehe ich nicht ganz. Nach meinem Verständnis rufe ich in der
asynchronen StartConfiguration durch Task.Run eine zweiten Thread auf der seine Arbeit wiederum synchron verarbeitet, da der await-Operator fehlt.
Wahrscheinlich wäre es besser wenn ich aus "async Task Configuration" einfach ein "void Configuration" mache.
Benutzte ich keinen Task.Run läuft der Code zwar durch, aber meine UI friert wieder ein.

T
461 Beiträge seit 2013
vor 5 Jahren

Hallo,

auf Grund des Fehlers könnte man annehmen, daß bei


        private void OpenTiaCommand_Executed(object sender, EventArgs e)
        {
            if(_Tia==null)
            {
                _Tia= new TiaPortal();
                _Projects=_Tia.Projects;
                _Project=_Projects.Open("Pfad");
            }
        }

das _Project längere Zeit in Verwendung ist und darauf zugegriffen wird, auch dann wenn


        private async Task Configuration()
        {
            if(_Tia==null)
            {
                _Tia= new TiaPortal();
                _Projects=_Tia.Projects;
                _Project=_Projects.Open("Pfad");
            }
            //HIER ENTSTEHT DIE EXCEPTION
            ProjectLibrary _projectLibrary = _Project.ProjectLibrary;


        }

ausgeführt wird.

Also müßte man es kontrollieren, daß wenn er bei private async Task Configuration() ankommt, das _Project=_Projects.Open("Pfad"); bei private void OpenTiaCommand_Executed(object sender, EventArgs e) noch in Arbeit ist.

Grüße

Ich habe den Titel mal angepasst, so dass Suchende auch etwas damit anfangen können. EDIT: Ich sollte beim Wort "Shift" im Titel das "f" nicht vergessen... 😄

D
D1990 Themenstarter:in
8 Beiträge seit 2019
vor 5 Jahren

Hallo Thomas, vielen Dank für deine Antwort !
Kannst du mir eine Tipp wie ich das am besten bewerkstelligen kann ?
Mein erster Verdacht wäre die Klasse Monitor zu nutzen, jedoch wüsste ich nicht wo ich dort am besten ansetzen würde.

Grüße !

D
D1990 Themenstarter:in
8 Beiträge seit 2019
vor 5 Jahren

Thread-übergreifender Betrieb
Der Zugriff auf Openness-Objekte ist nicht inhärent threadsicher.
Wenn Sie Multithreading zur Verbesserung der Performance Ihrer Openness-Anwendung
einsetzen, wird die Erstellung der Instanz des TIA Portals mit MTA empfohlen.
Wenn TIA Portal innerhalb eines STA-Thread erstellt oder angehängt wird, sollte auf alle
Openness-Objekte in Zusammenhang mit dieser Instanz des TIA Portals aus demselben STAThread
zugegriffen werden; andernfalls wird eine Ausnahme ausgelöst.

Da meines Wissens WPF Anwendungen immer in einem Single Thread Apartment gestartet werden, werde ich wohl einen anderen Weg finden müssen.
Gibt es die Möglichkeit alle Funktionen meiner Commands in einem Multi-Thread-Aparment ablaufen zu lassen ?
Oder alternativ meine UI über einen anderen Thread zu aktualisieren/ am Leben zu halten ?

D
D1990 Themenstarter:in
8 Beiträge seit 2019
vor 5 Jahren

Da meines Wissens WPF Anwendungen immer in einem Single Thread Apartment gestartet werden, werde ich wohl einen anderen Weg finden müssen.
Gibt es die Möglichkeit alle Funktionen meiner Commands in einem Multi-Thread-Aparment ablaufen zu lassen ?

Ok gut, Problem gelöst. Ich erstelle mein TIA Objekt nun in einem Task(MTA), das hat zu folge das ich es auch aus einem anderen Thread aufrufen kann. Demnach muss ich wohl zukünftig wesentlich mehr mit der ganzen Thematik auseiandersetzen, gerade im Bezug Threadsicherheit.
Ich werde mich dann nochmal in die Theorie stürzen 😃

Danke vielmals für die Tipps und Anregungen.