Statische Methode - Aufrufe durch mehrere Threads

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 1 Antwort in diesem Thema. Der letzte Beitrag () ist von CardinalXaXa.

    Statische Methode - Aufrufe durch mehrere Threads

    Hallo zusammen,

    ich habe mal eine Frage bezüglich statischer Methoden. Folgende Situation:
    Es läuft ein Server, welcher TCP/IP Verbindungen entgegen nimmt. Jede Verbindung läuft in seinem eigenen Thread. Nun gibt es eine Methode (SendVerwaltung), welche jedem verbundenen Mitarbeiter aktualisierte Listen zukommen lassen soll. Diese wird immer dann ausgelöst, wenn ein Client eine Verbindung aufbaut, die Verbindung trennt, oder eine Änderung an Mitarbeitern und Gruppen festgestellt wurde.

    Die Methode sieht wie folgt aus:

    C#-Quellcode

    1. internal static void SendVerwaltung()
    2. {
    3. if (ServerHilfsklasse.verwaltungsThread != null && ServerHilfsklasse.verwaltungsThread.IsAlive)
    4. ServerHilfsklasse.verwaltungsThread.Abort();
    5. ServerHilfsklasse.verwaltungsThread = new System.Threading.Thread(() =>
    6. {
    7. try
    8. {
    9. List<ServerThread> tmpClients = new List<ServerThread>(ServerHilfsklasse.ClientThreads);
    10. // Die drei Arrays sind für alle gleich und müssen dementsprechend nur einmal abgefragt werden
    11. AufgabenLibrary.Library.Mitarbeiter[] mitarbeiterAktiv = ServerFunktionen.GetMitarbeiter();
    12. AufgabenLibrary.Library.Mitarbeiter[] mitarbeiterAlle = ServerFunktionen.GetMitarbeiter(false);
    13. AufgabenLibrary.Library.Arbeitsgruppe[] arbeitsgruppen = ServerFunktionen.GetArbeitsgruppen();
    14. foreach (ServerThread st in tmpClients)
    15. {
    16. // Mehr Logik...
    17. serverProtokoll.Send(st.Connection);
    18. }
    19. AufgabenService.ServerLog.WriteLine("Verwaltung an alle Clients übermittelt.", LogTyp.INFO);
    20. }
    21. catch (System.Threading.ThreadAbortException)
    22. {
    23. AufgabenService.ServerLog.WriteLine("Übermittlung der Verwaltung wurde abgebrochen, da eine neuere Verwaltung existiert", LogTyp.WARNUNG);
    24. }
    25. catch (Exception ex)
    26. {
    27. AufgabenService.ServerLog.WriteLine($"#Sendverwaltung1 - {ex.Message}", LogTyp.FEHLER);
    28. }
    29. });
    30. ServerHilfsklasse.verwaltungsThread.Start();
    31. }


    tmpClients sind hier alle derzeit verbundenen User.

    Nun ruft die Methode weitere statische Methoden auf.
    GetMitarbeiter()
    GetMitarbeiter(false)
    GetArbeitsgruppen()

    Die Logik ist bei beiden Methoden die selbe, jeweils nur mit einer anderen Tabelle, Spalten und einem anderen return Wert.
    Der einfachheithalber hier die Methode GetArbeitsgruppen.

    C#-Quellcode

    1. internal static AufgabenLibrary.Library.Arbeitsgruppe[] GetArbeitsgruppen()
    2. {
    3. List<AufgabenLibrary.Library.Arbeitsgruppe> returnValue = new List<AufgabenLibrary.Library.Arbeitsgruppe>();
    4. using (FbConnection connection = new FbConnection(ServerHilfsklasse.FBConnectionString))
    5. {
    6. connection.Open();
    7. string sql = "SELECT DISTINCT PG_ID FROM PERSONALGRUPPEN ORDER BY PG_NAME ASC";
    8. FbCommand getGruppenIdsCmd = new FbCommand(sql, connection);
    9. FbDataReader reader = getGruppenIdsCmd.ExecuteReader();
    10. while (reader.Read())
    11. {
    12. int.TryParse(reader["PG_ID"].ToString(), out int GRUPPEN_ID);
    13. ServerArbeitsgruppe arbeitsgruppe = new ServerArbeitsgruppe(ServerHilfsklasse.FBConnectionString, GRUPPEN_ID);
    14. // Zur Liste hinzufügen, wir müssen das Objekt erst in einen Mitarbeiter des Typs Aufgabenlibrary.Library.Arbeitsgruppe umwandeln, da die Aufgabenplanung keine ServerArbeitsgruppe kennt.
    15. returnValue.Add(new AufgabenLibrary.Library.Arbeitsgruppe(arbeitsgruppe));
    16. }
    17. connection.Close();
    18. }
    19. return returnValue.ToArray();
    20. }


    In dieser Zeile hier:

    C#-Quellcode

    1. ServerArbeitsgruppe arbeitsgruppe = new ServerArbeitsgruppe(ServerHilfsklasse.FBConnectionString, GRUPPEN_ID);


    Wird durch den Konstruktor noch eine weitere Methode aufgerufen:
    SelectArbeitsgruppe()

    Diese Methode ist nicht statisch. Im Prinzip geschieht hier das selbe nochmal, nur ausführlicher. Siehe:

    C#-Quellcode

    1. while (reader.Read())
    2. {
    3. GRUPPEN_NAME = reader["PG_NAME"].ToString();
    4. int.TryParse(reader["PG_N_NR_CHEFF"].ToString(), out int _PG_N_NR_CHEFF);
    5. GRUPPENCHEF_ID = _PG_N_NR_CHEFF;
    6. // Lade den Namen des Gruppenchefs
    7. ServerMitarbeiter gruppenchef = new ServerMitarbeiter(FbConnectionString, GRUPPENCHEF_ID);
    8. GRUPPENCHEF_NAME = gruppenchef.USERNAME;
    9. }


    Das selbe geschieht auch nochmal für das Mitarbeiter Objekt, nur halt wieder andere Tabellen:

    C#-Quellcode

    1. ServerMitarbeiter gruppenchef = new ServerMitarbeiter(FbConnectionString, GRUPPENCHEF_ID);


    Sobald die wesentlichen Daten nun geladen sind, ruft SelectArbeitsgruppe() die Methode MitgliederRefresh() auf. Diese ist ebenfalls nicht statisch und Teil des GruppenObjekts.

    Diese fügt einfach alle IDs der Mitarbeiter zu einer Liste hinzu:

    C#-Quellcode

    1. public void MitgliederRefresh()
    2. {
    3. List<int> GruppenMitglieder = new List<int>();
    4. using (FbConnection DBVerbindung = new FbConnection(FbConnectionString))
    5. {
    6. DBVerbindung.Open();
    7. string getMitgliederSQL = "SELECT DISTINCT PERS_N_NR FROM P_2_PG WHERE PG_ID = @GRUPPEN_ID";
    8. FbCommand getMitgliederCMD = new FbCommand(getMitgliederSQL, DBVerbindung);
    9. getMitgliederCMD.Parameters.AddWithValue("@GRUPPEN_ID", this.GRUPPEN_ID);
    10. FbDataReader reader = getMitgliederCMD.ExecuteReader();
    11. while (reader.Read())
    12. {
    13. GruppenMitglieder.Add((int)reader["PERS_N_NR"]);
    14. }
    15. DBVerbindung.Close();
    16. }
    17. Mitglieder = GruppenMitglieder;
    18. }


    In dieser Methode passiert es nun meistens, dass beim öffnen der Datenbankverbindung eine AccessViolationException ausgelöst wird.
    DBVerbindung.Open();

    Mir erschließt sich allerings nicht genau wieso. Diese tritt immer sporadisch auf. Manchmal läuft es stundenlang gut und manchmal passiert es im Minutentakt.

    Hier mal der StackTrace:

    C#-Quellcode

    1. Frameworkversion: v4.0.30319
    2. Beschreibung: Der Prozess wurde aufgrund einer unbehandelten Ausnahme beendet.
    3. Ausnahmeinformationen: System.AccessViolationException
    4. bei FB_965910463_Class.isc_attach_database(IntPtr[], Int16, Byte[], FirebirdSql.Data.Client.Native.Handle.DatabaseHandle ByRef, Int16, Byte[])
    5. bei FB_965910463_Class.IFbClient.isc_attach_database(IntPtr[], Int16, Byte[], FirebirdSql.Data.Client.Native.Handle.DatabaseHandle ByRef, Int16, Byte[])
    6. bei FirebirdSql.Data.Client.Native.FesDatabase.Attach(FirebirdSql.Data.Common.DatabaseParameterBuffer, System.String, Int32, System.String, Byte[])
    7. bei FirebirdSql.Data.FirebirdClient.FbConnectionInternal.Connect()
    8. bei FirebirdSql.Data.FirebirdClient.FbConnection.Open()
    9. bei KarleyAufgabenServerDienst.Library.ServerArbeitsgruppe.MitgliederRefresh()
    10. bei KarleyAufgabenServerDienst.Library.ServerArbeitsgruppe.SelectArbeitsgruppe()
    11. bei KarleyAufgabenServerDienst.Library.ServerArbeitsgruppe..ctor(System.String, Int32)
    12. bei AufgabenServer.ServerFunktionen.GetArbeitsgruppen()
    13. bei AufgabenServer.ServerFunktionen+<>c.<SendVerwaltung>b__6_0()
    14. bei System.Threading.ThreadHelper.ThreadStart_Context(System.Object)
    15. bei System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
    16. bei System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
    17. bei System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
    18. bei System.Threading.ThreadHelper.ThreadStart()


    Mache ich etwas grundlegendes falsch? Vielleicht kann ja Jemand von euch Licht ins dunkel bringen.

    -Marvin