Moin moin
ich arbeite gerade daran, ein älteres Programm durch ein "Rebuild" zu modernisieren, von .Net FW4.6.1 zu .Net 8. Dabei möchte ich verschiedene Funktionen in externe DLLs auslagern. Die erste DLL habe ich bereits erfolgreich erstellt, und sie berechnet Durchschnittswerte. Die Hauptanwendung übermittelt dabei ein Jahres-Wert aus einer ComboBox an die DLL, welche die Berechnungen für die vorliegenden XML-Dateien vornimmt und die Ergebnisse zurückliefert.
Vorab habe ich mit einem kleinen Converter die alten XML-Dateien an das neue Modell in der DLL angepasst.
Nun habe ich jedoch gelesen, dass man bei der Arbeit mit DLLs besonders auf das Thema Threading und Multithreading achten muss. Ich verstehe allerdings noch nicht ganz, wann und warum ich beispielsweise so etwas wie ein
Könntet Ihr mir dabei helfen, den Zusammenhang besser zu verstehen und zu wissen, wann genau solche Synchronisationsmechanismen erforderlich sind?
Hier der Code der DLL(für .Net 8 und .Net FW 4.6.1 kompiliert)
Spoiler anzeigen
Spoiler anzeigen
ich arbeite gerade daran, ein älteres Programm durch ein "Rebuild" zu modernisieren, von .Net FW4.6.1 zu .Net 8. Dabei möchte ich verschiedene Funktionen in externe DLLs auslagern. Die erste DLL habe ich bereits erfolgreich erstellt, und sie berechnet Durchschnittswerte. Die Hauptanwendung übermittelt dabei ein Jahres-Wert aus einer ComboBox an die DLL, welche die Berechnungen für die vorliegenden XML-Dateien vornimmt und die Ergebnisse zurückliefert.
Vorab habe ich mit einem kleinen Converter die alten XML-Dateien an das neue Modell in der DLL angepasst.
Nun habe ich jedoch gelesen, dass man bei der Arbeit mit DLLs besonders auf das Thema Threading und Multithreading achten muss. Ich verstehe allerdings noch nicht ganz, wann und warum ich beispielsweise so etwas wie ein
SyncLock _lock
verwenden muss.Könntet Ihr mir dabei helfen, den Zusammenhang besser zu verstehen und zu wissen, wann genau solche Synchronisationsmechanismen erforderlich sind?
Hier der Code der DLL(für .Net 8 und .Net FW 4.6.1 kompiliert)
VB.NET-Quellcode
-
- Public Class WeatherData
- ' Keine Singleton-Instanz, da als Datencontainer für die XML-Inhalte verwendet wird!
- Public Property WeatherDay As String '' Keine Berechnungen aber zum speichern von neuen Daten erforderlich!
- Public Property WeatherNight As String '' Keine Berechnungen aber zum speichern von neuen Daten erforderlich!
- Public Property WeatherDate As DateTime
- Public Property RainDay As Double?
- Public Property SnowDay As Double?
- ' nicht alle aufgelistet !!
- End Class
- 'Klasse für Array von Wetterdaten
- Public Class WeatherDataArray
- Inherits List(Of WeatherData)
- End Class
VB.NET-Quellcode
-
- Public Class ClimaAverage
- ' Keine Singleton-Instanz, da als Datencontainer für die Berechnungen verwendet wird!
- Public Property Period As Integer
- Public Property AverageRain As Double?
- Public Property AverageSnowDay As Double?
- ' nicht alle aufgelistet !!
- Public Property EntryCount As Integer
- End Class
VB.NET-Quellcode
-
- Public Class ConfigService
- Inherits PropertyChange
- ' Singleton-Instanz, Threadsicher durch statische Initialisierung
- Public Shared ReadOnly Instance As New ConfigService()
- ' Pfad der späteren Hauptanwendung und Datenpfad für XML-Dateien
- Private Const MyBasePath As String = "WeatherApp" ' Für LogFiles, ConfigFiles etc.
- Private Const MyDataPath As String = "WeatherApp\Data"
- ' Definieren der Fehlermeldungen als Konstanten
- Private Const AccessDeniedMessage As String = "Zugriff verweigert für das Verzeichnis: {0}"
- Private Const DirectoryCreationErrorMessage As String = "Fehler beim Erstellen des Verzeichnisses: {0}"
- ' --- Eigenschaftsbereiche für Verzeichnisse ---
- Public ReadOnly Property BaseDirectory As String
- Get
- Return CreateDirectorySafe(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), MyBasePath))
- End Get
- End Property
- Public ReadOnly Property DataDirectory As String
- Get
- Return CreateDirectorySafe(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), MyDataPath))
- End Get
- End Property
- ' --- Sichere Verzeichniserstellungsmethode mit Fehlerbehandlung ---
- Private Function CreateDirectorySafe(path As String) As String
- Try
- If Not Directory.Exists(path) Then
- Directory.CreateDirectory(path)
- End If
- Return path
- Catch ex As UnauthorizedAccessException
- Throw New Exception(String.Format(AccessDeniedMessage, path), ex)
- Catch ex As Exception
- Throw New Exception(String.Format(DirectoryCreationErrorMessage, path), ex)
- End Try
- End Function
- ' --- Properties für dynamische Daten (XML-Dateien, Jahr, Monat) ---
- Private _xmlDataDirectory As String
- Public Property XmlDataDirectory As String
- Get
- If String.IsNullOrEmpty(_xmlDataDirectory) Then
- _xmlDataDirectory = DataDirectory
- End If
- Return _xmlDataDirectory
- End Get
- Set(value As String)
- If _xmlDataDirectory <> value Then
- _xmlDataDirectory = value
- OnPropertyChanged(NameOf(XmlDataDirectory))
- End If
- End Set
- End Property
- Private _currentYear As Integer
- Public Property CurrentYear As Integer
- Get
- Return _currentYear
- End Get
- Set(value As Integer)
- If _currentYear <> value Then
- _currentYear = value
- OnPropertyChanged(NameOf(CurrentYear))
- End If
- End Set
- End Property
- Private _currentMonth As Integer
- Public Property CurrentMonth As Integer
- Get
- Return _currentMonth
- End Get
- Set(value As Integer)
- If _currentMonth <> value Then
- _currentMonth = value
- OnPropertyChanged(NameOf(CurrentMonth))
- End If
- End Set
- End Property
- End Class
VB.NET-Quellcode
-
- Public Class CalculatedAverages
- ' Singleton-Instanz, Thread-sicher durch statische Initialisierung
- Public Shared ReadOnly Instance As New CalculatedAverages()
- ' Um die monatlichen Durchschnittswerte zu speichern
- Private ReadOnly _monthlyAverages As New List(Of ClimaAverage)()
- ' Dienste laden
- Private ReadOnly _configServiceInstance As ConfigService = ConfigService.Instance
- ' Getter für die monatlichen Durchschnittswerte
- Public Function GetMonthlyAverages() As List(Of ClimaAverage)
- Return _monthlyAverages
- End Function
- Public Function GetXmlFiles(selectedYear As Integer, month As Integer) As List(Of String)
- ' Formatieren von Jahr und Monat als String im Format "MM-YYYY.xml"
- Dim monthPattern As String = $"{month:00}-{selectedYear}.xml"
- Dim directoryPath As String = _configServiceInstance.XmlDataDirectory
- ' Debug: Ausgabe des Suchmusters
- Console.WriteLine($"Suche nach Dateien mit Muster: {monthPattern} in Verzeichnis: {directoryPath}")
- ' Abrufen der Dateien, die dem Muster entsprechen
- Dim files = Directory.GetFiles(directoryPath, monthPattern, SearchOption.TopDirectoryOnly).ToList()
- ' Debug-Ausgabe der gefundenen Dateien
- Console.WriteLine($"Gefundene Dateien für {month:00}-{selectedYear}: {String.Join(", ", files)}")
- Return files
- End Function
- Public Sub CallCalculation()
- _monthlyAverages.Clear()
- Console.WriteLine($"Berechnungen für das Jahr: {_configServiceInstance.CurrentYear}")
- For month As Integer = 1 To 12
- ' Aufruf von GetXmlFiles mit `currentYear` und `month`
- Dim files As List(Of String) = GetXmlFiles(_configServiceInstance.CurrentYear, month)
- If files.Count > 0 Then
- ' Summen und Zähler für die Berechnungen
- Dim monthlyRainDay As Double = 0
- Dim monthlySnowDay As Double = 0
- Dim monthlySnowNight As Double = 0
- Dim monthlyHumidiMinDay As Double = 0
- Dim monthlyHumidiMaxDay As Double = 0
- Dim monthlyHumidiMinNight As Double = 0
- Dim monthlyHumidiMaxNight As Double = 0
- Dim monthlyTempDay As Double = 0
- Dim monthlyTempNight As Double = 0
- Dim monthlyWindDay As Double = 0
- Dim monthlyWindNight As Double = 0
- Dim count As Integer = 0
- For Each filename As String In files
- Console.WriteLine($"Verarbeite Datei: {filename}")
- ' Wetterdaten deserialisieren
- Dim serializer As New XmlSerializer(GetType(WeatherDataArray))
- Using fs As New FileStream(filename, FileMode.Open)
- Dim data As WeatherDataArray = CType(serializer.Deserialize(fs), WeatherDataArray)
- If data.Count > 0 Then
- count += data.Count
- ' Werte summieren
- Dim rainSum As Double = CDbl(data.Sum(Function(wd) wd.RainDay))
- Dim snowDaySum As Double = CDbl(data.Sum(Function(wd) wd.SnowDay))
- Dim snowNightSum As Double = CDbl(data.Sum(Function(wd) wd.SnowNight))
- Dim humidiMinDaySum As Double = CDbl(data.Sum(Function(wd) wd.HumidiMinDay))
- Dim humidiMaxDaySum As Double = CDbl(data.Sum(Function(wd) wd.HumidiMaxDay))
- Dim humidiMinNightSum As Double = CDbl(data.Sum(Function(wd) wd.HumidiMinNight))
- Dim humidiMaxNightSum As Double = CDbl(data.Sum(Function(wd) wd.HumidiMaxNight))
- Dim tempDaySum As Double = CDbl(data.Sum(Function(wd) wd.TempDay))
- Dim tempNightSum As Double = CDbl(data.Sum(Function(wd) wd.TempNight))
- Dim windDaySum As Double = CDbl(data.Sum(Function(wd) wd.WindDay))
- Dim windNightSum As Double = CDbl(data.Sum(Function(wd) wd.WindNight))
- ' Zu den monatlichen Summen hinzufügen
- monthlyRainDay += rainSum
- monthlySnowDay += snowDaySum
- monthlySnowNight += snowNightSum
- monthlyHumidiMinDay += humidiMinDaySum
- monthlyHumidiMaxDay += humidiMaxDaySum
- monthlyHumidiMinNight += humidiMinNightSum
- monthlyHumidiMaxNight += humidiMaxNightSum
- monthlyTempDay += tempDaySum
- monthlyTempNight += tempNightSum
- monthlyWindDay += windDaySum
- monthlyWindNight += windNightSum
- Else
- Console.WriteLine($"Keine Wetterdaten in Datei {filename} gefunden.")
- End If
- End Using
- Next
- ' Durchschnittswerte berechnen
- If count > 0 Then
- Dim avgRain = Math.Round(monthlyRainDay / count, 2)
- Dim avgSnowDay = Math.Round(monthlySnowDay / count, 2)
- Dim avgSnowNight = Math.Round(monthlySnowNight / count, 2)
- Dim avgHumidiMinDay = Math.Round(monthlyHumidiMinDay / count, 2)
- Dim avgHumidiMaxDay = Math.Round(monthlyHumidiMaxDay / count, 2)
- Dim avgHumidiMinNight = Math.Round(monthlyHumidiMinNight / count, 2)
- Dim avgHumidiMaxNight = Math.Round(monthlyHumidiMaxNight / count, 2)
- Dim avgTempDay = Math.Round(monthlyTempDay / count, 2)
- Dim avgTempNight = Math.Round(monthlyTempNight / count, 2)
- Dim avgWindDay = Math.Round(monthlyWindDay / count, 2)
- Dim avgWindNight = Math.Round(monthlyWindNight / count, 2)
- _monthlyAverages.Add(New ClimaAverage() With {
- .Period = month,
- .AverageRain = avgRain,
- .AverageSnowDay = avgSnowDay,
- .AverageSnowNight = avgSnowNight,
- .AverageHumidiMinDay = avgHumidiMinDay,
- .AverageHumidiMaxDay = avgHumidiMaxDay,
- .AverageHumidiMinNight = avgHumidiMinNight,
- .AverageHumidiMaxNight = avgHumidiMaxNight,
- .AverageTempDay = avgTempDay,
- .AverageTempNight = avgTempNight,
- .AverageWindDay = avgWindDay,
- .AverageWindNight = avgWindNight
- })
- Else
- Console.WriteLine($"Monat {month}: Keine Daten gefunden, Durchschnittswerte werden nicht berechnet.")
- End If
- Else
- Console.WriteLine($"Keine Wetterdaten für {month:00}-{_configServiceInstance.CurrentYear} gefunden.")
- End If
- Next
- Console.WriteLine("Berechnungen abgeschlossen.")
- End Sub
- Public Function CalculateYearlyAverages() As ClimaAverage
- Dim yearlyAverage As New ClimaAverage With {
- .Period = _configServiceInstance.CurrentYear
- }
- If _monthlyAverages.Count > 0 Then
- Dim totalRain As Double = 0
- Dim totalSnowDay As Double = 0
- Dim totalSnowNight As Double = 0
- Dim totalHumidiMinDay As Double = 0
- Dim totalHumidiMaxDay As Double = 0
- Dim totalHumidiMinNight As Double = 0
- Dim totalHumidiMaxNight As Double = 0
- Dim totalTempDay As Double = 0
- Dim totalTempNight As Double = 0
- Dim totalWindDay As Double = 0
- Dim totalWindNight As Double = 0
- Dim countRain As Integer = 0
- Dim countSnowDay As Integer = 0
- Dim countSnowNight As Integer = 0
- Dim countHumidiMinDay As Integer = 0
- Dim countHumidiMaxDay As Integer = 0
- Dim countHumidiMinNight As Integer = 0
- Dim countHumidiMaxNight As Integer = 0
- Dim countTempDay As Integer = 0
- Dim countTempNight As Integer = 0
- Dim countWindDay As Integer = 0
- Dim countWindNight As Integer = 0
- For Each avg In _monthlyAverages
- totalRain += CDbl(avg.AverageRain)
- totalSnowDay += CDbl(avg.AverageSnowDay)
- totalSnowNight += CDbl(avg.AverageSnowNight)
- totalHumidiMinDay += CDbl(avg.AverageHumidiMinDay)
- totalHumidiMaxDay += CDbl(avg.AverageHumidiMaxDay)
- totalHumidiMinNight += CDbl(avg.AverageHumidiMinNight)
- totalHumidiMaxNight += CDbl(avg.AverageHumidiMaxNight)
- totalTempDay += CDbl(avg.AverageTempDay)
- totalTempNight += CDbl(avg.AverageTempNight)
- totalWindDay += CDbl(avg.AverageWindDay)
- totalWindNight += CDbl(avg.AverageWindNight)
- ' Inkrementieren der Zähler
- countRain += If(avg.AverageRain > 0, 1, 0)
- countSnowDay += If(avg.AverageSnowDay > 0, 1, 0)
- countSnowNight += If(avg.AverageSnowNight > 0, 1, 0)
- countHumidiMinDay += If(avg.AverageHumidiMinDay > 0, 1, 0)
- countHumidiMaxDay += If(avg.AverageHumidiMaxDay > 0, 1, 0)
- countHumidiMinNight += If(avg.AverageHumidiMinNight > 0, 1, 0)
- countHumidiMaxNight += If(avg.AverageHumidiMaxNight > 0, 1, 0)
- countTempDay += If(avg.AverageTempDay > 0, 1, 0)
- countTempNight += If(avg.AverageTempNight > 0, 1, 0)
- countWindDay += If(avg.AverageWindDay > 0, 1, 0)
- countWindNight += If(avg.AverageWindNight > 0, 1, 0)
- Next
- ' Durchschnittswerte berechnen
- yearlyAverage.AverageRain = Math.Round(totalRain / countRain, 2)
- yearlyAverage.AverageSnowDay = Math.Round(totalSnowDay / countSnowDay, 2)
- yearlyAverage.AverageSnowNight = Math.Round(totalSnowNight / countSnowNight, 2)
- yearlyAverage.AverageHumidiMinDay = Math.Round(totalHumidiMinDay / countHumidiMinDay, 2)
- yearlyAverage.AverageHumidiMaxDay = Math.Round(totalHumidiMaxDay / countHumidiMaxDay, 2)
- yearlyAverage.AverageHumidiMinNight = Math.Round(totalHumidiMinNight / countHumidiMinNight, 2)
- yearlyAverage.AverageHumidiMaxNight = Math.Round(totalHumidiMaxNight / countHumidiMaxNight, 2)
- yearlyAverage.AverageTempDay = Math.Round(totalTempDay / countTempDay, 2)
- yearlyAverage.AverageTempNight = Math.Round(totalTempNight / countTempNight, 2)
- yearlyAverage.AverageWindDay = Math.Round(totalWindDay / countWindDay, 2)
- yearlyAverage.AverageWindNight = Math.Round(totalWindNight / countWindNight, 2)
- Else
- Console.WriteLine("Keine monatlichen Durchschnittswerte vorhanden.")
- End If
- Return yearlyAverage
- End Function
- End Class
Asperger Autistin. Brauche immer etwas um gewisse Sachen zu verstehen.