Threads auf mehrere Kerne verteilen

  • VB.NET

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Threads auf mehrere Kerne verteilen

    Hallo,

    ich habe folgendes Problem:
    In Visual Basic 2010 erstelle ich mehrere Objekte vom Typ System.Threading.Thread.
    Diese Objekte werden alle (relativ) zeitgleich gestartet und rechnen dann auch ein paar Sekunden vor sich hin.

    Mir ist aber aufgefallen, dass die Anwendung trotz der vielen erstellten Threads nur 25 % der CPU ausnutzt (Habe einen i5 Dualcore mit Hyperthreading).
    In anderen Threads wurde ja immer gesagt, dass man einfach Threads erstellen muss und das Betriebssystem die dann selbst auf die Kerne verteilt, deswegen bin ich in meinem Fall etwas ratlos.

    Kann es sein, dass ich anstelle von Threads Tasks erstellen muss?

    Danke schon mal für Eure Hilfe!
    Danke schon mal für die Links.
    Ich schaue sie mir mal an.

    Hier der Code, falls einer von euch etwas auffälliges sieht:

    VB.NET-Quellcode

    1. Thread_GetSumme = New Thread(AddressOf RechneSummeFürToolbar)
    2. 'Die Einstellungen für die Threads werden definiert
    3. Thread_GetSumme.Name = "Thread_GetSumme"
    4. Thread_GetSumme.Priority = ThreadPriority.Lowest 'Niedrigste Priorität
    5. Thread_GetSumme.IsBackground = True
    6. Thread_GetSumme.Start()


    Ich definiere vier Threads auf diese Weise. Also der gleiche Code, nur mit anderen Objekten, kommt so vier mal hintereinander und wird auch ausgeführt.

    Nur die CPU-Last bleibt bei 25 %.
    Verzeihung ;)

    Hier die Methoden:

    VB.NET-Quellcode

    1. Sub RechneSummeFürToolbar()
    2. Dim MeinRechner As New Rechner 'Rechner-Klasse wird neu instanziert
    3. Dim TempSumme As Double
    4. TempSumme = MeinRechner.GetSumme(MeineSelektiertenZellen)
    5. If Me.InvokeRequired = True Then Me.Invoke(Sub() SchreibeToolbarSumme(TempSumme)) 'Methode zum Schreiben des Ergebnis' wird aufgerufen
    6. End Sub
    7. Sub SchreibeToolbarSumme(Summe As Double)
    8. ToolStripStatusLabel_Summe.Text = "Summe: " & Summe.ToString 'Ergebnis wird in das Label geschrieben
    9. End Sub
    Dafür Multithreading WTF???

    Große Berechnungen, die lange dauern können eventuell parallel schneller ablaufen. Aber sowas?!
    Du lässt im unklaren was GetSumme macht, aber so viel wird da wohl nicht gerechnet oder?

    Allein der Name RechneSummeFürToolbar ist schon irreführend. Der Name suggeriert, dass da was ausgerechnet wird und nicht irgendwelche GUI Control verändert werden.

    Ein Grund dafür, dass es gar nicht viel schneller laufen kann ist, weil du Invoke verwendest (was sich auch nicht vermeiden lässt). Invoke blockiert den aktuellen Thread und führt die Aufgabe im GUI-Thread aus.

    Der andere Grund ist, dass du natürlich immer den overhead von den Threads hast. Will heißen Threads lohnen sich erst, wenn die entsprechende Aufgabe lange genug ist. Dazu kommen natürlich noch so synchronisationsgeschichten und false sharing, was aber hier wahrscheinlich keine Rolle spielt.

    Wie lange ist denn die Laufzeit der Methode? 1 ms?

    Deine Annahme, dass verschiedene Threads auf verschiedenen (virtuellen) Kernen laufen können ist richtig.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „markus.obi“ ()

    Bei einer Laufzeit von 1 ms würde ich das tatsächlich nicht auf mehrere Threads auslagern.

    Auf die selbe Art wird noch Min, Max, Durchschnitt und Standardabweichung berechnet. Und zwar immer dann, wenn der Benutzer andere Zellen im Grid auswählt.
    --> Benutzer wählt x Zellen im Grid aus und bekommt in versch. Labels gleich Informationen dazu.


    Da das Grid durchaus ein paar Millionen Zellen beinhalten kann und das dann schon einiges an Rechenzeit benötigt, wollte ich das ganze eben (mit Multithreading) etwas flotter gestalten. ;)
    Vor allem soll der Benutzer gar nicht merken, dass etwas berechnet wird und zu jeder Zeit normal weiterarbeiten können.
    Es ist schon richtig, dass Threads von Windows auf die Kerne verzeilt werden. Ich weiss aber nicht, wie das genau von statten geht, jedefalls wird die Last aber nicht zwangslaeufig gleichmaessig verteilt. Es gibt auch Moeglichkeiten, einen Thread fuer einen bestimmten Kern zu erstellen, diese kenne ich zwar nicht, aber zum Glueck ist das im Framework schon durch die Parallel-Klasse geloest.
    Hi
    es gibt afaik zwei Sorten von Threads: Software-Threads und Hardware-Threads. Die Hardware-Threads kann man den Kernen zuordnen (sollte über ProcessThread modelliert werden), die anderen, glaube ich, übernimmt das Betriebssystem. (Keine Garantie auf Richtigkeit, hab' mich bisher nicht so damit beschäftigt ;)). Ich würde die Priorität allerdings vom ausführenden Thread erben lassen, nicht manuell auf Lowest setzen. Genügt dir eigentlich System.Threading.Tasks.Parallel nicht? Ich denk', dass die Informationen als Array oder Liste vorliegen, die über einen Index verarbeitet werden kann, also könnte man das so machen:

    VB.NET-Quellcode

    1. 'Voraussetzung: für Operator . gilt a . b . c = a . (b . c) = (a . b) . c
    2. Shared Function ParallelAggregation(Of TIn, TValue)(
    3. items() As TIn,
    4. valueSelector As Func(Of TIn, TValue),
    5. aggregation As Func(Of TValue, TValue, TValue),
    6. blockSize As Integer) As TValue
    7. If items Is Nothing Then Throw New ArgumentNullException("items")
    8. If valueSelector Is Nothing Then Throw New ArgumentNullException("valueSelector")
    9. If aggregation Is Nothing Then Throw New ArgumentNullException("aggregation")
    10. If items.Length = 0 Then Throw New ArgumentException("Items must provide at least one item.")
    11. Dim asyncResult(items.Length \ blockSize - 1) As TValue
    12. Dim result As TValue
    13. System.Threading.Tasks.Parallel.For(0, items.Length \ blockSize,
    14. Sub(i)
    15. Dim value As TValue = valueSelector(items(i))
    16. For j As Integer = i * blockSize + 1 To Math.Min((i + 1) * blockSize - 1, items.Length) - 1
    17. value = aggregation(value, valueSelector(items(i)))
    18. Next
    19. asyncResult(i) = value
    20. End Sub)
    21. result = asyncResult(0)
    22. For i As Integer = 1 To asyncResult.Length - 1
    23. result = aggregation(result, asyncResult(i))
    24. Next
    25. Return result
    26. End Function
    27. Shared Function ParallelSum(values() As Integer, blockSize As Integer) As Integer
    28. If values Is Nothing Then Throw New ArgumentNullException("values")
    29. If values.Length = 0 Then Return 0
    30. Dim asyncResult(values.Length \ blockSize - 1) As Integer
    31. Dim result As Integer
    32. System.Threading.Tasks.Parallel.For(0, values.Length \ blockSize,
    33. Sub(i)
    34. Dim sum As Integer = values(i * blockSize)
    35. For j As Integer = i * blockSize + 1 To Math.Min((i + 1) * blockSize - 1, values.Length) - 1
    36. sum += values(i)
    37. Next
    38. asyncResult(i) = sum
    39. End Sub)
    40. result = asyncResult(0)
    41. For i As Integer = 1 To asyncResult.Length - 1
    42. result += asyncResult(i)
    43. Next
    44. Return result
    45. End Function

    Sollte doch eigentlich funktionieren, oder? Funktioniert übrigens mit beliebigen Listen, die einen parallelen Zugriff über Index erlauben (z.B. IList(Of T))

    Gruß
    ~blaze~

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „~blaze~“ ()

    Wurde schon angesprochen: Scheinbar ist Threading hier jehnseitsmäßiger Overkill.
    Weiters gut möglich, dass auch in Rechner.GetSumme per Invoking an den Gui-Thread zurück-delegiert wird, sodass der thread tatsächlich garnix zu tun hat. Das würde jdfs. die Problembeschreibung aus post#1 aufklären.