Source Code mit Nutzung con Com-Objejten deutlich langsmer im Thread

  • VB.NET
  • .NET (FX) 4.0

Es gibt 96 Antworten in diesem Thema. Der letzte Beitrag () ist von Tukuan.

    Tukuan schrieb:

    Soll ich die Objekte mit Nothing "Vernichten"?
    Diese Objekte sollten eigentlich eine Dispose()-Methode implementieren. Gibt es da ein Quit() oder Close() oder so?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Wie gesagt: Com-Objekte zu bereinigen ist eine Wissenschaft - zumindest mit Office-Interop-Com-Objekten ist das so.
    Quit() - wenn vorhanden - reicht nicht, sondern man muss mit Marshal.FinalReleaseComObjekt() da rumfuhrwerken.

    Aber vlt. ists bei dem famosen Famos-Teil nicht so - bei Office ist ja das Problem, dass beim InterOp-Zugriff die Office-Anwendung unsichtbar gestartet wird, und wenn nicht jedes Popel-Cömmchen ordentlich beerdigt wird, dann bleibt die Office-Anwendung unsichtbar im Speicher, und ist nurnoch mittm TaskManager abzuschießen.

    RodFromGermany schrieb:

    Diese Objekte sollten eigentlich eine Dispose()-Methode implementieren. Gibt es da ein Quit() oder Close() oder so?

    Hab mal geschaut. Nein leider nicht. Jedenfalls nicht für die ImcCoreLib.DChannel.

    ErfinderDesRades: Unnötiges Vollzitat entfernt
    Das mit Office habe ich auch schon festgestellt.
    Also heißt es so wenig Objekte aus der Schnittstelle zu verwenden wie möglich verwenden.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Wegen der Performance. Meine Vermutung ist, dass es an den COM Aufrufen hängt. Trotzdem jetzt mal einen Profiler ranhängen.
    Gibt viele gute Profiler. Je nachdem welche VS Version du hast, hat Visual Studio schon einen drinnen. Ansonsten bekommst du bei z.B. Redgate eine gratis Testversion für 30 Tage (sollte ja reichen).
    Nimm also einen Profiler und schau mal genau nach wo das Problem ist. Speichere dann am besten das Ergebnis und fang an zu optimieren. Je nachdem kann man bei den Berechnungen selbst noch etwas optimieren. Aber ich kann mir kaum vorstellen, dass es daran scheitert.
    Deshalb wäre es gut du zeigst mal den Code der COM-Aufrufe. Eventuell instanzierst du COM-Objekte öfter als notwendig. Zudem ist es leider Fakt, dass Microsoft bei .NET und COM einfach sehr viel vermurkst hat. Was mit nativem Code recht gut funktioniert ist in .NET oftmals einfach nur wesentlich langsamer oder geht gar nicht oder nur auf komplizierten umwegen. Das ganze verhält sich bei C++/CLI anders. Du könntest also theoretisch auch versuchen die COM-Schnittstellen über C++/CLI anzusprechen da es dort im Hintergrund anders abläuft als bei reinem VB.NET. Dazu gäbe es noch Alternativen welche ich z.B. auch bei CSCore verwende. Wird dann jedoch schon sehr kompliziert und ist fraglich ob sich das rentiert.


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.

    Tukuan schrieb:

    Also heißt es so wenig Objekte aus der Schnittstelle zu verwenden wie möglich verwenden.
    jo, zumindest das. Kannst ja auch mal im Taskmanager gugge, ob da iwelche Prozesse hochfahren, wenn mitte Lib gearbeitet wird.
    Langfristig musste auch auf Memory-Leaks testen, also nicht, dass deine Anwendung über die Zeit den Arbeitsspeicher mit unreleasten Com-Objekten füllt, und nach 4h stürzt vlt. sogar eine ganz annere Anwendung ab deswegen.

    Themawechsel: Wer sagt eiglich, man könne in einem Prozess nur einen Sta-Thread (was immer das sein mag) haben?

    VB.NET-Quellcode

    1. Dim runner As ThreadStart = Sub()
    2. Thread.Sleep(500)
    3. MessageBox.Show("done")
    4. End Sub
    5. Dim thr = New Thread(runner)
    6. thr.SetApartmentState(ApartmentState.STA)
    7. thr.Start()

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    thefiloe schrieb:

    Gibt viele gute Profiler. Je nachdem welche VS Version du hast, hat Visual Studio schon einen drinnen.

    Mein VS2010 Prof. hat keinen... Oder ich übersehe ihn.
    Leider dürfen wir nur bestimmt SW installieren. Muss ich mal schauen, was es da so gibt. Oder ich muss es mal auf einem anderen Rechner versuchen, wo ich mehr Freiheiten habe. Aber da muss dann erst ne VB Version rauf...

    thefiloe schrieb:

    Zudem ist es leider Fakt, dass Microsoft bei .NET und COM einfach sehr viel vermurkst hat.

    Wenn das so ist, was nimmt man den Heutzutage für Bibliotheken...

    ErfinderDesRades schrieb:

    Langfristig musste auch auf Memory-Leaks testen,

    Das ist ein guten Hinweis. Werde ich bei den finalen Tests bestimmt drauf achten. Das Programm soll nachher tagelang laufen.

    ErfinderDesRades schrieb:

    Wer sagt eiglich, man könne in einem Prozess nur einen Sta-Thread (was immer das sein mag) haben?

    Niemand - nur landet der neue STA-Thread in einem neuen STA, denn ein STA kann immer nur einen Thread beherbergen. Aber ein Prozess kann mehrere STAs haben. Das ändert jedoch nichts daran, dass zwischen den Apartments trotzdem gemarshallt werden muss.

    Der Profiler ist bei VS 2010 erst ab Premium dabei (siehe download.microsoft.com/downloa…ure-Comparison-Matrix.pdf). Schade - der wäre jetzt echt hilfreich.

    Es gibt eine 30-Tage-Testversion von Visual Studio 2012 Ultimate - dort ist der Profiler garantiert dabei. Installiere sie in eine virtuelle Maschine (oder frag' deinen Arbeitgeber nach einem VS-Upgrade).

    COM ist zwar alt, aber immer noch aktuell. Microsoft hat z.B. die neue WinRT-Umgebung auf COM aufgesetzt. Es macht also durchaus Sinn, sich damit zu beschäftigen.

    Ich halte COM übrigens nicht für vermurkst - es ist einfach eine Möglichkeit, Bibliotheken einheitlich anzusprechen. Sicherlich ist der obligatorische Registryeintrag für jede Komponente nicht sonderlich gut gewählt (man denke an den allgegenwärtigen Fehler "Klasse nicht registriert"), aber das wurde in .NET behoben, weil sich Assemblies nun selbst beschreiben.

    Eine simple C-DLL ist die einfachste Lösung für eine Bibliothek, aber so geht natürlich die Objektorientierung verloren. Eine .NET-Assembly wäre schon toll, aber wenn nicht alles neu implementiert werden soll, wird der Hersteller einfach einen COM-Wrapper ausliefern, den du auch selbst generieren kannst.
    Gruß
    hal2000
    Also Thema Geld wurde ja angerissen - das mit "den Kunden dickere Rechner kaufen lassen" finde ich iwie arrogant, sondern ein intelligenter Chef sieht doch zu, dass seine Entwickler ständig das optimale Werkzeug haben.
    Das Geld etwa für eine Edition mit Profiler oder auch für einen externen Profiler hat man doch schon wieder drin, wenn man dadurch das Produkt auch nur 2 Tage früher ausliefern kann, oder?
    Ganz zu schweigen von den Kosten, die nachträgliche Patches verursachen - da brauchste doch nur einen genervten Kunden verlieren, und schon kannste nachrechnen: am falschen Ende gespart.
    Verstehen wir uns nicht falsch. COM an sich finde ich ein sehr gutes Konzept. Das Problem dabei ist nur die Verwendung in .NET. Teilweise fehlen Aspekte in .NET und schaffen dadurch Einschränkungen. Als Beispiel kann ich da das InterfaceType-Attribut anführen. Es können dort drei verschiedene "Typen" von Interfaces angegeben werden (IUnknown, IDispatch, Dual). Was jedoch komplett fehlt ist einfach etwas wie "None". Es gibt COM-Objekte (welche sogar von MS selbst stammen) welche weder noch sind. Jetzt hast du in .NET keine Chance diese Objekte zu verwenden, da dir .NET immer irgendwelche Funktionspointer reinmurkst welche falsch sind. Einziger Workaround: .NET die Teile reinmurksen lassen und diese anschließend die Vtable des Objektes wieder zu überschreiben. Das meine ich mit vermurkst (hätte noch viele andere Beispiele). Zum anderen sind die Aufrufe aus .NET recht langsam und machen manchmal mit dem Apartment Marshaling Dinge die kein Mensch nachvollziehen kann.
    Bin deshalb nach langem hin und her dazu übergegangen die ganzen COM Aufrufe "manuell" zu machen, da ich da wenigstens weiß was ich mache.


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.

    thefiloe schrieb:

    Bin deshalb nach langem hin und her dazu übergegangen die ganzen COM Aufrufe "manuell" zu machen, da ich da wenigstens weiß was ich mache.

    Klingt ja gut. Und wie macht man das? Oder wäre das die Version mit einem C++ Projekt ohne .NET in einer dll?

    Angenommen ich entschließe mich dazu das ganze Hanlding in ein dll (C++ Projekt ohne .NET) zu packen:
    a) Bingt das sicher einen Rechengewinn?
    b) Würde man es dann ähnlich machen: ein Thread ruft die dll auf oder ist das dann nicht doppelt gemoppelt?
    c) Macht man das dann in C++ oder würde es auch in VB gehen? C++ Syntax ist schon so lange her...
    d) Gibt es Tutorial (sicherlich aber kennt ihr ein gutes), wie man so was macht: Erstellen einer lib, einbinden in den Sourcecode, aufrufen mit Parameterübergabe, Rückgabe von Werten?

    ErfinderDesRades schrieb:

    am falschen Ende gespart.

    Da magst du recht haben. Ich muss mal schauen, was es bei uns da so gibt. Ist leider nicht so einfach. Aber meinem Boss bekomme ich schon überzeugt :)
    Im übrigen muss das Programm nicht nur bei mir das ganze schnell abarbeiten. Es muss auch auf Messrechnern laufen, die ggf. weniger Rechenpower haben.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Tukuan“ ()

    Nein ist ohne C++. Das C++/CLI ist auch nicht wirklich C++. Ist wie eine Zwischenschicht zwischen .NET und C++. Meine Variante ist komplett ohne C++ reines C#/VB. Ist jedoch sehr aufwendig. Also bitte zeig doch mal wie du jetzt die COM-Objekte aufrufst. Hast du bis jetzt noch immer nicht gezeigt.


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
    googelt doch nochmal die "rules of optimization".

    Ich denke nämlich, jetzt kommt der Einstieg in sehr schwierige, schwer wartbare und anfällige Optimierungen, und solch soll man ja bekanntlich auf die lange Bank schieben, und ohne Profiler schoma garnet anfassen.

    Also googelt doch nochmal die "rules of optimization".

    thefiloe schrieb:

    Also bitte zeig doch mal wie du jetzt die COM-Objekte aufrufst. Hast du bis jetzt noch immer nicht gezeigt.

    Verstehe ich nicht ganz...

    VB.NET-Quellcode

    1. Dim Famos_File As New ImcCoreLib.DFile
    2. Famos_File.Open(_SourceFile, ImcCoreLib.DmFileOptionConstants.cdmFileQuickLoad)
    3. Famos_File.Close()

    Wenn ich es bisher noch nicht komplett falsch verstanden habe, ist das ein zugriff auf die imc Com Schnittstelle ImcCoreLib. Oder?

    ErfinderDesRades schrieb:

    Also googelt doch nochmal die "rules of optimization".

    Du meinst das:
    The "rules" of optimising are a rhetorical device intended to dissuade
    novice programmers from cluttering up their programs with vain attempts
    at writing optimal code. They are:
    FirstRuleOfOptimization - Don't.
    SecondRuleOfOptimization - Don't... yet.
    ProfileBeforeOptimizing

    OK. Profiling... Muss ich mal schauen, wie ich das hinbekomme... Mein VS hat das nicht (muss mir also die andere Version besorgen...) und externe Tools darf ich wohl erst mal nicht nutzen.
    Aber das Projekt hat zig tausend zeilen verteilt auf mehrere Module und Klassen. Überall mit auf die Schnittstelle zugegriffen.
    Soll ich ein kleines Projekt erstellen, dass nur diese oben erwähnte Berechnung ausführt?
    Hier der Aufruf aus einem Windows-Form:

    VB.NET-Quellcode

    1. If Not AsThread.Checked Then
    2. harm.CalculateHarmonics()
    3. Else
    4. harm.HarmCalcThread.Priority = ThreadPriority.Value
    5. harm.HarmCalcThread.Start()
    6. End If

    und hier der komplette Sourcecode zur Berechnung. Ich habe einiges raus geschmissen und versucht mit den Resourcen schonend und mit nicht so viel "new" zu verwenden.

    VB.NET-Quellcode

    1. Imports System.Threading
    2. ''' <summary>
    3. ''' Class for harmonics calculation
    4. ''' Store results ungrouped in 5 Hz steps with ampitude and phase.
    5. ''' </summary>
    6. ''' <remarks></remarks>
    7. Public Class DACalculateHarmonics
    8. Public HarmCalcThread As New Thread(AddressOf CalculateHarmonics) '_OLD)
    9. Private Property SourceFile As String
    10. Private channelNames() As String
    11. Public Sub New(ByVal _channelNames() As String,
    12. ByVal _SourceFile As String)
    13. channelNames = _channelNames
    14. SourceFile = _SourceFile
    15. End Sub
    16. Public Sub CalculateHarmonics()
    17. If Not IO.File.Exists(SourceFile) Then
    18. MsgBox(SourceFile & " not found")
    19. Exit Sub
    20. End If
    21. Dim stopwatch As New Stopwatch()
    22. stopwatch.Start()
    23. Console.WriteLine(stopwatch.ElapsedMilliseconds & " - Start")
    24. 'FamosObjekt
    25. Dim MyFamos As New ImcFamosLib.Famos
    26. MyFamos.ResultDataFormat = ImcFamosLib.FsResultFormatConstants.cfsFormatDouble
    27. MyFamos.Spec.FFTMode = ImcFamosLib.FsFFTModeConstants.cfsModeAppend
    28. MyFamos.Spec.FFTWindow = ImcFamosLib.FsFFTWindowConstants.cfsWinRectangle
    29. Dim Famos_File As New ImcCoreLib.DFile
    30. Dim window As Integer = 10
    31. Dim channelRead(channelNames.Length) As Double
    32. Dim ZerosCalc(channelNames.Length) As Double
    33. Dim FFTCalc(channelNames.Length) As Double
    34. For i = 0 To channelNames.Length - 1
    35. Dim sw As New Stopwatch
    36. sw.Start()
    37. Dim raw As ImcCoreLib.DChannel
    38. Famos_File.Open(_SourceFile, ImcCoreLib.DmFileOptionConstants.cdmFileQuickLoad)
    39. raw = GetFamosChannelFromFile(Famos_File, channelNames(i))
    40. Famos_File.Close()
    41. 'Console.WriteLine(stopwatch.ElapsedMilliseconds & " - channel read" & " - " & i)
    42. channelRead(i) = sw.ElapsedMilliseconds
    43. If IsNothing(raw) Then
    44. MsgBox("Channel " & channelNames(i) & " not found")
    45. Else
    46. 'raw.TransferToFamos()
    47. Dim samples As Integer = 8192
    48. If raw.xDelta = 0.00005 Then
    49. 'samples rate 20000
    50. samples = 4096
    51. End If
    52. 'Filter parameter: Butterworth,-,8th order, cut of freq. = 100 Hz
    53. Dim rawResample As ImcCoreLib.DChannel = MyFamos.Filter.LowPass(raw, 0, 0, 8, 100)
    54. 'rawResample.Name = raw.Name & "_lowpass"
    55. 'rawResample.TransferToFamos()
    56. Dim rawZeros As ImcCoreLib.DChannel = CalculateZeroCrossings(MyFamos, raw, rawResample, window)
    57. 'rawZeros.Name = raw.Name & "_Zeros"
    58. 'rawZeros.TransferToFamos()
    59. 'Console.WriteLine(stopwatch.ElapsedMilliseconds & " - zeros calc." & " - " & i)
    60. ZerosCalc(i) = sw.ElapsedMilliseconds
    61. Dim part As ImcCoreLib.DChannel
    62. Dim spec As ImcCoreLib.DChannel
    63. For j = 1 To rawZeros.Length - 1
    64. part = MyFamos.Edit.CopyPartX(raw, rawZeros.Value(j), rawZeros.Value(j + 1))
    65. spec = CalculateFFT(MyFamos, part, (rawZeros.Value(j + 1) - rawZeros.Value(j)), samples)
    66. Next
    67. Console.WriteLine(stopwatch.ElapsedMilliseconds & " - FFTs calc. " & " - " & i)
    68. FFTCalc(i) = sw.ElapsedMilliseconds
    69. End If
    70. sw.Stop()
    71. Next
    72. Dim t As New ImcCoreLib.DChannel
    73. t.AppendDataDouble(channelNames.Length, CType(channelRead, System.Array))
    74. Dim min As Double = MyFamos.Stat.Min(t).Value(1)
    75. Dim max As Double = MyFamos.Stat.Max(t).Value(1)
    76. Dim mean As Double = MyFamos.Stat.Mean(t).Value(1)
    77. Dim rms As Double = MyFamos.Stat.RMS(t).Value(1)
    78. Console.WriteLine("Channel read; {0};{1};{2};{3}", min, max, mean, rms)
    79. t = New ImcCoreLib.DChannel
    80. t.AppendDataDouble(channelNames.Length, CType(ZerosCalc, System.Array))
    81. min = MyFamos.Stat.Min(t).Value(1)
    82. max = MyFamos.Stat.Max(t).Value(1)
    83. mean = MyFamos.Stat.Mean(t).Value(1)
    84. rms = MyFamos.Stat.RMS(t).Value(1)
    85. Console.WriteLine("Zeros calced; {0};{1};{2};{3}", min, max, mean, rms)
    86. t = New ImcCoreLib.DChannel
    87. t.AppendDataDouble(channelNames.Length, CType(FFTCalc, System.Array))
    88. min = MyFamos.Stat.Min(t).Value(1)
    89. max = MyFamos.Stat.Max(t).Value(1)
    90. mean = MyFamos.Stat.Mean(t).Value(1)
    91. rms = MyFamos.Stat.RMS(t).Value(1)
    92. Console.WriteLine("FFT calced; {0};{1};{2};{3}", min, max, mean, rms)
    93. Console.WriteLine(stopwatch.ElapsedMilliseconds & " - finished")
    94. Console.WriteLine("######################################################################################################################################################################################")
    95. stopwatch.Stop()
    96. End Sub
    97. Private Function CalculateZeroCrossings(ByVal MyFamos As ImcFamosLib.Famos,
    98. ByVal RawChannel As ImcCoreLib.DChannel,
    99. ByVal FilteredChannel As ImcCoreLib.DChannel,
    100. ByVal Fenster As Integer) As ImcCoreLib.DChannel
    101. Dim HilfChan As ImcCoreLib.DChannel = MyFamos.Edit.CopyPartX(RawChannel, 1, 1.04)
    102. Dim Nullsthilf As ImcCoreLib.DChannel = MyFamos.Stat.SearchLevel(HilfChan, 4, 0, 0, 0, 0, 0, 1)
    103. Nullsthilf.CmpY = Nullsthilf.CmpX
    104. HilfChan = MyFamos.Edit.CopyPartX(FilteredChannel, 1, 1.04)
    105. Dim NUllsthilf_f As ImcCoreLib.DChannel = MyFamos.Stat.SearchLevel(HilfChan, 4, 0, 0, 0, 0, 0, 1)
    106. NUllsthilf_f.CmpY = NUllsthilf_f.CmpX
    107. FilteredChannel.xOffset = -1 * (NUllsthilf_f.Value(2) - Nullsthilf.Value(2)) 'Offsetanpassen damit Filter Kanal über Rohkanal liegt
    108. Dim Nullstellen As ImcCoreLib.DChannel = MyFamos.Stat.SearchLevel(FilteredChannel, 2, 0, 0, 0, 0, 0, 1)
    109. 'Dim Nullstellen2 As New ImcCoreLib.DChannel
    110. Dim Nullst As Integer
    111. Dim Nullhilf As Double
    112. Dim Laenge As Integer
    113. Dim Periode As Double = 0.02
    114. Dim j As Integer
    115. Laenge = Nullstellen.Length
    116. Nullstellen.CmpY = Nullstellen.CmpX
    117. HilfChan = New ImcCoreLib.DChannel
    118. 'Filtering of ZeroCrossings so that only "Full Periods" are included in resulting channel
    119. For i = 1 To (Laenge - Fenster) Step Fenster
    120. j = 0
    121. Nullst = 0
    122. Nullhilf = 0
    123. Do While Nullst < Fenster
    124. If (Nullstellen.Value(i + j + 1) - Nullstellen.Value(i + j)) >= (0.75 * Periode) Then
    125. Nullhilf = Nullstellen.Value(i + j + 1)
    126. Nullst = Nullst + 1
    127. End If
    128. j = j + 1
    129. Loop
    130. HilfChan.AppendData(Nullhilf)
    131. Next i
    132. Return HilfChan
    133. End Function
    134. Private Function CalculateFFT(ByVal MyFamos As ImcFamosLib.Famos,
    135. ByVal RawChannel As ImcCoreLib.DChannel,
    136. ByVal deltaT As Double,
    137. ByVal Samples As Integer) As ImcCoreLib.DChannel
    138. Dim xDelta As Double = 1 / (Samples / deltaT)
    139. Dim Famos_Uref As ImcCoreLib.DChannel = MyFamos.Edit.Ramp(0, xDelta, Samples)
    140. Dim Famos_Channel As ImcCoreLib.DChannel = MyFamos.Misc.IPol3(RawChannel, 1)
    141. Famos_Channel = MyFamos.Misc.ResampleRef(Famos_Channel, Famos_Uref)
    142. Famos_Channel = MyFamos.Spec.FFTa(Famos_Channel)
    143. Return Famos_Channel 'MyFamos.Spec.FFTa(Famos_Channel)
    144. End Function
    145. ''' <summary>
    146. ''' Get Channel from imc Famos file. The given Famos file must be opened from calling function.
    147. ''' </summary>
    148. ''' <param name="famosFile">imc Famos DFile object - file must be opened</param>
    149. ''' <param name="ChannelName">Demanded channel name; If channel is inside a group naming has to be "Groupname:Channelname"</param>
    150. ''' <returns>Returns found channel or NOTHING</returns>
    151. ''' <remarks></remarks>
    152. Public Function GetFamosChannelFromFile(ByVal famosFile As ImcCoreLib.DFile,
    153. ByVal ChannelName As String) As ImcCoreLib.DChannel
    154. For Each c As ImcCoreLib.DChannel In famosFile.DChannels
    155. If c.Name = ChannelName Then
    156. Return c
    157. End If
    158. Next
    159. Return Nothing
    160. End Function
    161. End Class


    PS: Ich werde wohl in den nächsten Tagen nicht ganz so oft online sein können...

    Tukuan schrieb:

    harm.HarmCalcThread.Start()

    setzt aber keinen Appartment Thread?
    Weil für mich sieht das eigt. soweit gut aus(Natürlich müssen die ganzen COM-Objekte noch richtig released werden, besonders gefährlich sind solche stellen):

    VB.NET-Quellcode

    1. Famos_Channel = MyFamos.Misc.ResampleRef(Famos_Channel, Famos_Uref)
    2. Famos_Channel = MyFamos.Spec.FFTa(Famos_Channel)

    da du hier die Referenz überschreibst, aber das zugrunde liegende COM-Objekt nie released wird, vlt. solltest du hier einfach mit einer zweiten Referenz arbeiten...

    Und warum diesen Teil nicht so? Ist find ich besser lesbar(und auch etwas schneller, wenn auch vernachlässigbar)

    VB.NET-Quellcode

    1. If AsThread.Checked Then
    2. harm.HarmCalcThread.Priority = ThreadPriority.Value
    3. harm.HarmCalcThread.Start()
    4. Else
    5. harm.CalculateHarmonics()
    6. End If
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    @Tukuan Wenn der Algorithmus läuft, schmeiß alle Console.WriteLine(...) raus.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    entferne mal endlich den blöden Microsoft.Visualbasic - Namespace, und benutze die allgemeinen Framework-Klassen, statt des vb6-Abwärtskompatibel-Schrotts: Böse Funktionen vermeiden

    und guck ma, ob son DChanel-Dingens nicht eine Clear-Methode hat, dann kannste dasselbe vlt. mehrmals verwenden.

    Welchen Datentyp hat ChannelRead?
    Warum konvertierst du das nach Array?