Threadsicherheit herstellen

  • VB.NET

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

    Threadsicherheit herstellen

    hi,
    ich steh momentan total auf dem Schauch...
    leider hab ich kein vergleichbares Beispiel gefunden...
    ich versuche gerade ein Programm (was funktionsfähig ist) umzuschreiben das es über Threads läuft.
    ich verwende dazu den Backgroundworker1 / 2 ... usw. um Hintergrundthreads auszulösen.

    folgendes Problem hab ich nun:

    irgendwo im Quellcode löse ich ein Ereignis aus wo dann steht:

    VB.NET-Quellcode

    1. 'starte Backgroundworker
    2. If BackgroundWorker_kopiereTAGV1aufV2.IsBusy = False Then
    3. BackgroundWorker_kopiereTAGV1aufV2.RunWorkerAsync()
    4. End If


    auf der gleichen Form wird jetzt das DoWork aufgefufen

    und da erstelle ich aus einer Listbox eine Liste

    VB.NET-Quellcode

    1. 'Liste mit ausgewählten Dateien erzeugen
    2. Dim Liste As New List(Of String)
    3. Dim Schleife As UInteger = 0
    4. For Each Eintrag As String In ListBox2_Dateien.SelectedItems()
    5. 'kompletter Pfad und Dateiname in die Liste schreiben
    6. Liste.Add(My.Computer.FileSystem.CombinePath(AktuellerPfad, Eintrag))
    7. Next


    nur da springt der Compiler mir raus - threadübergreifender vorgang.....

    meine Frage:
    1. wie bekomm ich das weg?
    2. warum passiert das an einer anderen Stelle nicht wo ich dies ähnlich mache.. und zwar so:

    VB.NET-Quellcode

    1. For Each Pfad As String In ListBox1_Ordnerliste_zusammenführen.Items
    2. For Each Datei As String In My.Computer.FileSystem.GetFiles(Pfad, FileIO.SearchOption.SearchAllSubDirectories)
    3. 'nur die Dateien in die Liste aufnehmen welche die ausgeählten Endungen haben
    4. If LCase(Form1.hole_dateiinfos(Datei).Extension) = Label_Dateierweiterung.Text Then
    5. Dateiliste.Add(Datei)
    6. End If
    7. Next
    8. Next


    damit ist es mir noch nicht 1x rausgesprungen.... ist eigentlich nur 1x mehr eine for each drumherum.....

    mit delegate würd ich arbeiten wenn ich wüsste wie ich die liste dann ins dowork bekomm!!!

    vielleicht kann mir ja jemand mal einen Denkanstoss verpassen... wäre sehr dankbar!
    okay.. begininvoke hatte ich mir schon angeschaut... wie gesagt.. ich steh momentan auf dem schlauch.


    wie soll ich das einbinden?

    muss ich die liste nun im dowork des Backgroundworkers erstellen oder in einer eigenen SUB und das dann dem DoWork übergeben?
    Du könntest dir natürlich noch ein paar kleine Subs machen, ungefähr so:

    VB.NET-Quellcode

    1. Delegate Sub ListAdd(ByVal l As ListBox, ByVal str As String)
    2. Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    3. Me.BeginInvoke(New ListAdd(AddressOf Add), ListBox1, "Test")
    4. End Sub
    5. Sub Add(ByVal l As ListBox, ByVal str As String)
    6. l.Items.Add(str)
    7. End Sub
    ich hab das problem das ich ja nicht eine listbox beschreiben möchte sondern
    aus den Markierungen die einträge auslesen will.

    das macht mir ja so kopfzerbrechen...

    nochmal:
    1. ich drücke einen Knopf
    2. beim click ereignis starte ich den backgroundworker wenn min 1 Eintrag in einer Listbox markiert ist
    3. dowork wird ausgeführt
    4. die Einträge werden / sollen im Dowork sortiert werden bzw. kopiert
    --> die Einträge sind Dateien und das kopieren dauert rel. lange... deswegen der BW!
    5. die listbox bleibt unverändert...
    Ich habe im Showroom ein Tutorial zu BackgroundWorkern. Vielleicht hilft Dir das weiter.

    Aber zum Thema:
    Zum Übergeben der ausgewählten Einträge aus der ListBox an den BGW solltest Du nicht direkt auf die ListBox zugreifen (also aus dem BGW Thread heraus), sondern beim Aufrufen von .RunWorkerAsync als Parameter ein Array von String übergeben, in dem die ausgewählten Einträge der ListBox stehen (wie das geht wird im Tutorial erklärt).

    Beim Zugreifen vom BGW aus auf den GUI Thread solltest Du .ReportProgress verwenden.
    Ich weiß nicht was Du noch alles vom BGW Thread aus machst außer der ListBox Einträge hinzuzufügen, aber wenn es nur das ist reicht es so aus den Code geringfügig umzuschreiben:

    VB.NET-Quellcode

    1. Liste.Add(My.Computer.FileSystem.CombinePath(AktuellerPfad, Eintrag))

    ersetzen durch

    VB.NET-Quellcode

    1. BackgroundWorker_kopiereTAGV1aufV2.ReportProgress(Nothing, IO.Path.Combine(AktuellerPfad, Eintrag))
    2. '1. Was ist das für ein Name???
    3. '2. Vermeide das My Namespace. Das wird hier im Forum zehntausend Mal angesprochen und ich möchte nur verhindern,
    4. 'dass es Dir noch zehntausend Mal an den Kopf geworfen wird ;)

    Dadurch wird im GUI Thread die Sub ausgeführt, die das Event BackgroundWorker_kopiereTAGV1aufV2.ProgressChanged handelt.
    (Wie hinzufügen steht im Tutorial)
    In e.UserState steht dann der Eintrag, der zur Liste hinzugefügt wird. In die Sub schreibst Du nur noch

    VB.NET-Quellcode

    1. Liste.Add(e.UserState.ToString)


    Warum der Fehler beim dritten Codebeispiel nicht auftritt weiß ich nicht. Wird Zeile 5 überhaupt erreicht? Oder wurde Dateiliste im BGW Thread deklariert?

    PS: Der Debugger springt Dir raus ;)
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    hi
    @Niko Ortner
    danke erstmal für die Mühe ... auch um 4.52 Uhr!!!

    das trifft aber nicht ganz meine Aufgabe!
    ich schriebe ja nix in eine Listbox sondern lese dort nur aus und verwende dann eine Generic List
    also so:

    dim Liste as new List(of string)

    darin befinden sich nun meine Dateien welche ich kopieren /verschieben oder sonst was machen will....

    report Progress wird in der for schliefe im dowork benutzt
    und im report Progress ereignis ändere ich auch auf einem Dialog die prozenzzahl (also den Fortschritt)

    etwas so:

    VB.NET-Quellcode

    1. Dialog_bitte_warten.Label_Prozent.Text = (e.ProgressPercentage.ToString() & "%")


    alles okay bis hierhin

    hier mal mein kompletter Anfang aus dem Do Work

    VB.NET-Quellcode

    1. Private Sub BackgroundWorker_kopiereTagV2aufV1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BGW_kopiereTagV2aufV1.DoWork
    2. 'Liste mit ausgewählten Dateien erzeugen
    3. Dim Liste As New List(Of String)
    4. Dim Schleife As UInteger = 0
    5. For Each Eintrag As String In ListBox2_Dateien.SelectedItems()
    6. 'kompletter Pfad und Dateiname in die Liste schreiben
    7. Liste.Add(My.Computer.FileSystem.CombinePath(AktuellerPfad, Eintrag))
    8. Next
    9. 'Liste umkehren
    10. Liste.Reverse()
    11. 'Anzahl der Dateien ermitteln
    12. Dim Anzahl_Dateien As UInteger = Liste.Count
    13. 'jetzt aus jeder Datei den TAG V2 auf TAG V1 versuchen zu kopieren
    14. For Each Datei As String In Liste
    15. 'Schleifenzähler hochzählen
    16. Schleife += 1
    17. 'fortschritt bekannt geben
    18. BGW_kopiereTAGV1aufV2.ReportProgress((Schleife * 100) / Anzahl_Dateien)
    19. 'Abbruchbedingung festlegen
    20. If (BGW_kopiereTAGV1aufV2.CancellationPending = True) Then
    21. e.Cancel = True
    22. Exit For
    23. End If
    24. ....jetzt tu noch ein bisschen was und nehme dann den nächsten Eintrag aus der erstellten Liste bis zum bitteren ende


    das heißt doch für mich das ich garnicht im GUI Thread schreiben wollte... nur mal nachschauen welche Einträge markiert sind.
    Ok, hoffe ich hab es dieses Mal richtig verstanden.

    VB.NET-Quellcode

    1. Delegate Sub Add(Byval l As ListBox)
    2. Private Function List(ByVal l As ListBox)
    3. Dim Liste As New List(Of String)
    4. For Each Eintrag As String In l.SelectedItems()
    5. Liste.Add(My.Computer.FileSystem.CombinePath(AktuellerPfad, Eintrag))
    6. Next
    7. Return Liste
    8. End Function
    9. Private Sub BackgroundWorker_kopiereTagV2aufV1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BGW_kopiereTagV2aufV1.DoWork
    10. 'Liste mit ausgewählten Dateien erzeugen
    11. Dim Liste As New List(Of String)
    12. Dim Schleife As UInteger = 0
    13. Liste.AddRange(Me.BeginInvoke(New Add(AddressOf List), ListBox2))
    14. 'Liste umkehren
    15. Liste.Reverse()
    16. 'Anzahl der Dateien ermitteln
    17. Dim Anzahl_Dateien As UInteger = Liste.Count
    18. 'jetzt aus jeder Datei den TAG V2 auf TAG V1 versuchen zu kopieren
    19. For Each Datei As String In Liste
    20. 'Schleifenzähler hochzählen
    21. Schleife += 1
    22. 'fortschritt bekannt geben
    23. BGW_kopiereTAGV1aufV2.ReportProgress((Schleife * 100) / Anzahl_Dateien)
    24. 'Abbruchbedingung festlegen
    25. If (BGW_kopiereTAGV1aufV2.CancellationPending = True) Then
    26. e.Cancel = True
    27. Exit For
    28. End If
    29. '.....
    30. End Sub
    RunWorkerAsync() hat eine Überladung, die ein Object entgegen nimmt. Übergebe dort die SelectedItems der ListBox.
    In DoWork kannst du es so abfragen:

    VB.NET-Quellcode

    1. Dim selectedItems As SelectedObjectCollection = DirectCast(e.Argument, SelectedObjectCollection)


    Viele Grüße, Phil.
    danke schön!
    das funktioniert erstmal, ich wusste garnicht das man den RunWorkerAsync überladen kann.

    habs jetzt mal so übergeben:

    aus einer X-beliebigen SUB wird

    dim Zusammenstellung as new List(of string)

    erstellt.

    dann in Zusammenstellung die gewünschten Einträge reingehauen

    und dann der BGW gestartet mit
    etwa so:
    BGW.runWorkerAsync(Zusammenstellung)


    im DoWork musste ich jetzt hat noch eine Liste definieren

    dim Liste as new List(of String)

    und dann einfach das Argument in die liste zu übertragen

    Liste = e.Argument

    kannst du mir evt. den Sinn des SelectedObjectCollection und DirectCast erklären?
    ist das nötig oder nur sinnvoll?!?
    ListBox.SelectedItems gibt dir ein Obejkt vom Typ SelectedObjectCollection zurück. RunWorkerAsync() nimmt ein simples Object entgegen und DoWorkEventArgs.Arguemtn ist ebenfalls vom Typ Object. Deshalb muss man es mit DirectCast() wieder zurück casten in die SelectedObjectCollection, die man eigentlich übergeben hat.
    Nötig ist es nur, wenn Option Strict On ist, aber das sollte sie sowieso immer sein.

    Viele Grüße, Phil.