[VB.NET] Threads laten samenwerken...

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • 430xlkod
  • Registratie: Januari 2007
  • Laatst online: 06-12-2024
Hallo dudes,

Heb even wat hulp nodig met volgend probleem.
In het formulier staat een START button en een PROGRESSBAR.
Ik heb een i3 550 en wil daarom met 4 loops door een sequentie gaan.

Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
Imports System
Imports System.IO
Imports System.Net
Imports System.Threading

Public Class Form1

    Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
        Dim thread1 As Threading.Thread
        thread1 = New Thread(AddressOf Me.functie1)
        thread1.Start()

        Dim thread2 As Threading.Thread
        thread2 = New Thread(AddressOf Me.functie2)
        thread2.Start()

        Dim thread3 As Threading.Thread
        thread3 = New Thread(AddressOf Me.functie3)
        thread3.Start()

        Dim thread4 As Threading.Thread
        thread4 = New Thread(AddressOf Me.functie4)
        thread4.Start()

    End Sub

    Public Sub functie1()
        Dim min As Integer = 100000
        Dim max As Integer = 250000

        For teller = min To max Step 1
            ProgressBarTotal.PerformStep()
        Next
    End Sub

    Public Sub functie2()
        Dim min As Integer = 250001
        Dim max As Integer = 500000

        For teller = min To max Step 1
            ProgressBarTotal.PerformStep()
        Next
    End Sub

    Public Sub functie3()
        Dim min As Integer = 500001
        Dim max As Integer = 750000

        For teller = min To max Step 1
            ProgressBarTotal.PerformStep()
        Next
    End Sub

    Public Sub functie4()
        Dim min As Integer = 750001
        Dim max As Integer = 999999

        For teller = min To max Step 1
            ProgressBarTotal.PerformStep()
        Next
    End Sub
End Class


Maar ik krijg dan de error:
"Cross-thread operation not valid: Control 'ProgressBarTotal' accessed from a thread other than the thread it was created on."

Heb gezocht op google maar komt er eerlijk gezegd niet echt wijs aan uit, mede doordat ik dit nog (of misschien niet) krijg op school.

Graag wat uitleg en hulp is welkom!

EDIT:

ProgressBarTotal properties staan op:
Minimum -> 100000
Maximum -> 999999
Step -> 1

[ Voor 3% gewijzigd door 430xlkod op 05-03-2011 12:40 ]


Acties:
  • 0 Henk 'm!

  • PaulZ
  • Registratie: Augustus 2004
  • Laatst online: 21-05-2024
Je mag die ProgressBar dus niet updaten vanuit een andere thread.
Kijk eens naar delegates (bijv hier of in de help) om dat wél voor elkaar te krijgen

Vlinders moet je volgen, niet vangen...


Acties:
  • 0 Henk 'm!

  • Feanathiel
  • Registratie: Juni 2007
  • Niet online

Feanathiel

Cup<Coffee>

Wat PaulZ zegt, een control aanpassen kan alleen vanuit dezelfde thread. De bijbehorende tutorial van MSDN zal op de volgende pagina te vinden zijn:

MSDN: How to: Make Thread-Safe Calls to Windows Forms Controls

Acties:
  • 0 Henk 'm!

  • 430xlkod
  • Registratie: Januari 2007
  • Laatst online: 06-12-2024
PaulZ, daar was ik ondertussen ook al uit, maar ik snap niet concreet hoe ik dat moet implementeren hierin.

EDIT:

Opgelost :) .

Visual Basic:
1
2
3
4
5
6
7
Private Sub updateBar()
        If Me.InvokeRequired Then
            Me.Invoke(New MethodInvoker(AddressOf updateBar))
        Else
            Me.ProgressBarTotal.PerformStep()
        End If
End Sub


En de sub updateBar() roep ik dan op in elke aparte thread.

[ Voor 60% gewijzigd door 430xlkod op 05-03-2011 13:13 ]


Acties:
  • 0 Henk 'm!

  • 430xlkod
  • Registratie: Januari 2007
  • Laatst online: 06-12-2024
Oké, volgend probleem :) .
Hoe doe ik dit nu maar dan met een parameter in de sub?

Als ik copy paste doe maar met een parameter, dan geeft ie een error op lijn 3.
Voorbeeld:
Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Private Sub updateLog(ByVal URL As String)
        If Me.InvokeRequired Then
            Me.Invoke(New MethodInvoker(AddressOf updateLog))
        Else
            txtBoxLijst.AppendText(URL & vbCrLf)
        End If
    End Sub

'------------------------------------------------------------------

Public Sub functie1()
        Dim href as String = "TEST"
        updateLog(href)
End Sub


Alvast bedankt als iemand mij op de goede weg kan helpen. Ondertussen probeer ik zelf ook door!

[ Voor 4% gewijzigd door 430xlkod op 05-03-2011 17:23 ]


Acties:
  • 0 Henk 'm!

  • Mavamaarten
  • Registratie: September 2009
  • Nu online

Mavamaarten

Omdat het kan!

Zijn Backgroundworkers niet handig in dit geval ? Die kunnen gewoon progress reporten :)

Android developer & dürüm-liefhebber


Acties:
  • 0 Henk 'm!

  • 430xlkod
  • Registratie: Januari 2007
  • Laatst online: 06-12-2024
Inderdaad, daar had ik ook over gelezen. Het probleem echter is dat ik sinds vandaag eingelijk in mijn uppie bezig ben met threaden. Ik ken er dus absoluut niks van! Maar het begint me wel te dagen hoe het in elkaar zit.
Heb de oplossing daarom met trail&error gevonden:
Visual Basic:
1
2
3
4
5
6
7
Private Sub updateLog(ByVal URL As String)
        If Me.InvokeRequired Then
            Me.Invoke(New ParameterizedThreadStart(AddressOf updateLog), URL)
        Else
            txtBoxLijst.AppendText(URL & vbCrLf)
        End If
    End Sub

Acties:
  • 0 Henk 'm!

  • 430xlkod
  • Registratie: Januari 2007
  • Laatst online: 06-12-2024
Probleempje weer :).
Ik heb het even overnieuw gedaan, maar ik heb nu het probleem dat het formulier niet reageert. Ik heb er vanzelfsprekend een STOP button op, maar hij is zo 'druk' bezig met door die for loop te gaan dat niks erop reageert. Zelfs een label die aangeeft hoever het staat reageert niet, enkel mijn progressbar veranderd wel.
EDIT: Label veranderd nu wel, heb bij de performstep van de bar ook lbl.Refresh gedaan.

Maar de knop stop en alles reageert dus niet, ik krijg het formulier ook niet gesloten via "X". Bedoeling van threads is toch dat het bestuurbaar blijft nee? Een backgroundworker heb ik ook geprobeerd, zelfde probleem :) .

Acties:
  • 0 Henk 'm!

  • TallManNL
  • Registratie: Oktober 2005
  • Laatst online: 18-05 13:52
Wat voor code heb je onder je stopknop gehangen om je threads mee af te breken, die rekenen vrolijk door totdat ze of klaar zijn of totdat jij ze stopt. Zelfde voor je 'X', die moet ook checken of de threads nog actief zijn en ze stoppen.

geheelonthouder met geheugenverlies


Acties:
  • 0 Henk 'm!

  • 430xlkod
  • Registratie: Januari 2007
  • Laatst online: 06-12-2024
Achter de stop heb ik:
Visual Basic:
1
2
3
4
5
draad.Abort()
Dim msgbox As Integer = MessageBox.Show("Afsluiten?", "Info:", MessageBoxButtons.OKCancel)
If (msgbox = vbOK) Then
    Me.Close()
End If


Maar het probleem is gewoon, dat zodra hij bezig is, niks meer wil reageren. Ik kan het hele formulier niet verplaatsen, minimaliseren, maximaliseren, sluiten... Enkel mijn progressbar en label met de status hoever hij is (die in de code word geupdate elke lus) die verandert.

[ Voor 3% gewijzigd door 430xlkod op 06-03-2011 19:56 ]


Acties:
  • 0 Henk 'm!

  • eek
  • Registratie: Februari 2001
  • Laatst online: 06-04-2020

eek

@MagickNET

430xlkod schreef op zondag 06 maart 2011 @ 19:19:
Probleempje weer :).
Ik heb het even overnieuw gedaan, maar ik heb nu het probleem dat het formulier niet reageert.
Overnieuw betekend ook dat je andere code hebt dan in je eerste post? Wat heb je aan de code veranderd?

Skill is when luck becomes a habit.


Acties:
  • 0 Henk 'm!

  • 430xlkod
  • Registratie: Januari 2007
  • Laatst online: 06-12-2024
De functie heb ik hetzelfde overgenomen, enkel heb ik de lay-out veranderd en de kleine prullen rondom.
Om te updaten en dergelijke dingen had ik voorheen allerlei kleine functies maar dat zit nu allen in één functie.

Het zit voor 90% hetzelfde in elkaar, ook de werking en volgorde is hetzelfde, toch wil hij niet reageren. Zelfs als ik de functie laat uitvoeren door een backgroundworker. En dan te verstaan dat ik eerst 4 threads liet lopen die totaal 1.000.000 aan loops deden, en nu maar 1 loop van 100.000 loops en krijg dit. Maar zelfs bij een loop van 500 "hangt" het formulier al vast.

EDIT:
Ter illustratie...
Afbeeldingslocatie: http://imageupload.org/dm-1312994469530.png

[ Voor 14% gewijzigd door 430xlkod op 06-03-2011 22:45 ]


Acties:
  • 0 Henk 'm!

  • Gomez12
  • Registratie: Maart 2001
  • Laatst online: 17-10-2023
Je progressbar hangt in je main thread, die moet je minder vaak updaten.

In wezen heb je nu 4 threads die enkel rekenen en tegelijkertijd je main thread aanspreken om het screen te updaten, tja dat vind je main thread niet leuk. Die heeft momenteel meer te doen dan alle losse threads.

Gewoon enkel je progressbar bijwerken als het echt noodzakelijk is ( bijv 1x per 100 thread-operaties )

Acties:
  • 0 Henk 'm!

  • 430xlkod
  • Registratie: Januari 2007
  • Laatst online: 06-12-2024
Gomez12, dat zou wel eens kunnen werken. Echter kan ik me herinneren dat bij de vorige versie dat ik had, hij dat ook deed updaten per loop (en dit zelfs 4 keer zoveel omdat in elke thread (4 stuks) een loop liep en deed updaten)... (kan het even niet controleren omdat hij al verwijderd is).

Maar je hebt wel een punt. Ga ik even aan werken om dat te doen.

(PS: Er rekent nu maar één thread. Vorige versie had 4 threads, sorry voor de onduidelijkheid :) .)

EDIT:
Principe zou dit moeten werken dan. Moet even wachten dat hij klaar is met loopen (hij is momenteel bezig, dus kan niet builden).
Visual Basic:
1
2
3
4
If ((i Mod 100) = 0) Then
     pgrBarTotal.PerformStep()
     lblVoortgang.Refresh()
End If


EDIT2:
Heeft niet mogen baten... Zelfs bij een grotere stap (1000) werkt het nog niet.
Even verder zoeken. Mocht iemand raad weten, laat maar horen!

[ Voor 40% gewijzigd door 430xlkod op 06-03-2011 22:44 ]


Acties:
  • 0 Henk 'm!

  • Gomez12
  • Registratie: Maart 2001
  • Laatst online: 17-10-2023
Heb je nog steeds die 200.000 threadswitches erin zitten om je mod 100 te kunnen uitvoeren?

want threadswitches zijn nog steeds "duur". In principe moet je thread gewoon zijn gewoon gang en alleen iets melden als het zinnig is, niet 100.000 keer een threadswitch doen.

Acties:
  • 0 Henk 'm!

  • TallManNL
  • Registratie: Oktober 2005
  • Laatst online: 18-05 13:52
Zorg dat je thread zelf alleen naar je main thread callt om de x berekeningen. Zo'n invoke naar je mainthread is nogal zinloos als je mainthread erna toch niet update.

Verder valt het op dat je de volgende constructie gebruikt.
Visual Basic .NET:
1
2
3
4
5
...
        If Me.InvokeRequired Then 
            Me.Invoke(New ParameterizedThreadStart(AddressOf updateLog), URL) 
        Else 
...

Je rekenthread gaat op je invoke call staan te wachten tot je UI is geupdate. UI updaten is traag en die workerthread staat hierdoor de grootste deel van de tijd te wachten.
Visual Basic .NET:
1
2
3
4
5
...
        If Me.InvokeRequired Then 
            Me.BeginInvoke(New ParameterizedThreadStart(AddressOf updateLog), URL) 
        Else 
...

Deze code vraagt alleen aan je main thread om updateLog uit te voeren, echter wordt er niet gewacht op de uitvoering, je workerthread gaat meteen door.
disclaimer, ik heb de docs even niet bekeken, het kan zijn dat de aanroep van begininvoke iets anders is qua parameters.

geheelonthouder met geheugenverlies


Acties:
  • 0 Henk 'm!

  • 430xlkod
  • Registratie: Januari 2007
  • Laatst online: 06-12-2024
Oké, ik snap jullie punt, maar dat is mijn oude code. De nieuwe wordt enkel in één functie uitgevoerd.
Kortom:
- Ik vul gegevens in (start waarde, en eind waarde)
- Ik druk op start
- Intern word er een thread gedeclareerd, en gestart en deze voert 1 functie uit.
- Die functie loopt (als alles goed is ingevuld, zit controle op) door aantal loops (eind waarde - start waarde)
- In die functie wordt er om de loop een performstep gedaan naar de progressbar, en label word gerefreshed met juiste info (werkt allemaal perfect, reageert direct).
- Hij is klaar en ik kan afsluiten.

Probleem is dus, tijdens zijn loop kan ik voor de rest niks doen. Zelfs niet als ik in de loop met mod 100 om de 100 loops hem laat updaten. Er is ook geen verschil tussen Me.Invoke of Me.BeginInvoke, want er word maar één functie éénmaal opgeroepen.
In het kort de code:
Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
        draad = New Thread(AddressOf Me.bereken)
        draad.Start()
    End Sub

    Private Sub btnStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStop.Click
        draad.Abort()
        Dim msgbox As Integer = MessageBox.Show("Afsluiten?", "Info:", MessageBoxButtons.OKCancel)
        If (msgbox = vbOK) Then
            Me.Close()
        End If
    End Sub

    Private Sub bereken()
        If Me.InvokeRequired Then
            Me.Invoke(New MethodInvoker(AddressOf bereken))
            'Me.BeginInvoke(New MethodInvoker(AddressOf bereken))
        Else
            '... rim ram code met de for - loop
        End If
    End Sub


En dat is eigenlijk alles...

[ Voor 36% gewijzigd door 430xlkod op 07-03-2011 10:40 ]


Acties:
  • 0 Henk 'm!

  • TallManNL
  • Registratie: Oktober 2005
  • Laatst online: 18-05 13:52
En dat is dus eigenlijk precies fout, Je workerfunctie (bruteforce) gooi je nu meteen weer naar de mainthread. Je moet losse, kleine functies hebben om je UI mee te updaten die je binnen je loop aanroept.
Dan draait je hele loop op je workerthread en alleen de UI update op de mainthread.
Nu draai je dus alles op de mainthread. En dan heb je ook geen verschil meer tussen Invoke en BeginInvoke

Versimpeld (uitgecomment wat je niet op je thread kan doen, in principe alles naar UI controls/User):
Visual Basic .NET:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
    Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click 
                pgrBarTotal.Minimum = min 
                pgrBarTotal.Maximum = max 
        draad = New Thread(AddressOf Me.bereken) 
        draad.Start() 
    End Sub 

    Private Sub btnStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStop.Click 
        draad.Abort() 
        Dim msgbox As Integer = MessageBox.Show("Afsluiten?", "Info:", MessageBoxButtons.OKCancel) 
        If (msgbox = vbOK) Then 
            Me.Close() 
        End If 
    End Sub 

    Private Sub UpdateUI()
        If Me.InvokeRequired Then 
            Me.Invoke(New MethodInvoker(AddressOf UiUpdate)) 
            'Me.BeginInvoke(New MethodInvoker(AddressOf bereken)) 
        Else 
            pgrBarTotal.PerformStep() 
        end if

    end sub

    Private Sub bereken() 
            Try 
                btnStart.Enabled = False 
                btnSave.Enabled = False 
                Dim min As Integer = txtStart.Text 
                Dim max As Integer = txtEinde.Text 
                Dim href As String = txtBaseURL.SelectedItem 
                Dim ext As String = txtExtensionURL.SelectedItem 


                txtLog.AppendText(Now() + ": Started..." + vbCrLf) 

                For i = min To max Step 1 
                    Dim percentage As Int16 = ((i - min) / (max - min)) * 100 
                    Math.Round(percentage, 4) 
'                    lblVoortgang.Text = (i - min) & " van " & (max - min) & " | " & percentage.ToString & "%" 

                    Dim url As String = (href.ToString + i.ToString + ext.ToString) 
                    Dim request As HttpWebRequest = WebRequest.Create(url) 
'                    pgrBarTotal.PerformStep() 
'                    lblVoortgang.Refresh() 
                    Try 
                        Dim response As HttpWebResponse = request.GetResponse() 
                        If (response.StatusCode <> 0) Then 
'                            txtLog.AppendText(Now() + ": " + url + vbCrLf + response.StatusCode.ToString() + vbCrLf) 
                        End If 
                    Catch ex As Exception 
                        '... laat de exception gewoon gaan. Mogelijke oplossing -> ex.dispose? 
                    End Try 
                Next 
'                btnStart.Enabled = True 
'                btnSave.Enabled = True 
'                txtLog.AppendText(Now() + ": Klaar... | " + min.ToString() + " tot " + max.ToString() + vbCrLf) 
            Catch ex As Exception 
                draad.Abort() 
'                txtLog.AppendText(Now() + ": Error ergens..." + vbCrLf) 
'                MessageBox.Show("Vergeten url en/of extensie in te geven?", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error) 
            End Try 
    End Sub 

[ Voor 79% gewijzigd door TallManNL op 07-03-2011 10:40 ]

geheelonthouder met geheugenverlies


Acties:
  • 0 Henk 'm!

  • 430xlkod
  • Registratie: Januari 2007
  • Laatst online: 06-12-2024
Hoe kan ik de mainthread dan ook apart laten draaien?
Ik dacht dus als ik voor de functie een thread declareer en toeken, dat het dan automatisch is gesplitst...

EDIT:

Ik snap wat je bedoelt. Even kijken als ik het kan toepassen (en werkend :+ ).

[ Voor 22% gewijzigd door 430xlkod op 07-03-2011 10:42 ]


Acties:
  • 0 Henk 'm!

  • TallManNL
  • Registratie: Oktober 2005
  • Laatst online: 18-05 13:52
Je mainthread draait al. Wat je wilt doen is zoveel mogelijk op je workerthread laten uitvoeren. Zodra je een Me.Invoke doet ga je terug naar je mainthread en dat wil je dus zo min mogelijk doen. Ik heb hierboven een voorbeeld van gemaakt, ik zie nu alleen dat ik de UpdateUI niet aanroep. Deze roep je aan waar je je progressbar.performstep aanriep.

geheelonthouder met geheugenverlies


Acties:
  • 0 Henk 'm!

  • 430xlkod
  • Registratie: Januari 2007
  • Laatst online: 06-12-2024
Maar in principe moet ik dan bijna voor elke 'verandering' in de UI die in de functie gebeurt een aparte sub gaan maken?
- een sub met een string als parameter voor het log te updaten
- een sub zonder parameter om progressbar.performstep te doen en label te refreshen
- een sub om die messagebox op te gooien...
- een sub om buttons de enablen/disablen (kan nog in één gedaan worden met een boolean)

Is dat dan niet helemaal omslachtig, of werk ik dan verkeerd?

Acties:
  • 0 Henk 'm!

  • TallManNL
  • Registratie: Oktober 2005
  • Laatst online: 18-05 13:52
Je kunt volgens mij een aantal UI updates combineren, lblvoortgang.text, pgrBarTotal.PerformStep, lblVoortgang.Refresh kan onder een en dezelfde functie. Je kan gewoon meerder waarden aan je update functie doorgeven, je zal dan wel een delegate nodig hebben voor je BeginInvoke.

Maar je zal als je vanuit je thread veel wil laten zien op de UI inderdaad wel daarvoor de juiste helper functies nodig hebben.

geheelonthouder met geheugenverlies


Acties:
  • 0 Henk 'm!

  • 430xlkod
  • Registratie: Januari 2007
  • Laatst online: 06-12-2024
Oké, dus ik zit wel in de juiste richting. Ik ga even alles omzetten nog, zogoed als klaar al maar krijg hier en daar nog wat error's.
Je kan met zo een delegate meer dan één parameter aan een functie meegegeven? Aangezien een MethodInvoker geen parameters aanvaard, en een ParameterizedThreadStart maar één parameter.

EDIT:
Cross-threading exception op:
Dim href As String = txtBaseURL.SelectedItem
Dim ext As String = txtExtensionURL.SelectedItem

Binnen de functie zelf... Moet ik daar dan ook al een invoked getter voor maken?

[ Voor 25% gewijzigd door 430xlkod op 07-03-2011 11:22 ]


Acties:
  • 0 Henk 'm!

  • TallManNL
  • Registratie: Oktober 2005
  • Laatst online: 18-05 13:52
Net zitten rondzoeken, schijnbaar zitten in .NET 4.0 anonymous delegates voor vb.net. Dat geeft je wat mogelijkheden om de helperfuncties wat te versimpelen.
Deze anonymous functions kun je namelijk aan je BeginInvoke doorgeven.
Visual Basic .NET:
1
2
Dim p As String = "Hallo"
Me.BeginInvoke(New MethodInvoker(Sub() Debug.WriteLine("{0}", p)))


Anders heb je volgende mogelijkheid nog om e.e.a. op te lossen.
Visual Basic .NET:
1
2
3
4
5
6
7
8
9
        Private Delegate Sub MyUIUpdateDelegate(ByVal arg1 As String, ByVal arg2 as Integer)
        Private MyUIUpdateDel As MyUIUpdateDelegate = AddressOf UIUpdate
        Private Sub UIUpdate(ByVal arg1 As String, ByVal arg2 as Integer)
            If Me.InvokeRequired Then
                Me.BeginInvoke(OnActiveProcessEventDel, arg1, arg2)
            Else
                'Update UI
            End If
        End Sub


EDIT: Voor je exception zou ik die waardes als parameters aan je thread meegeven. ParameterizedThreadStart daarvoor gebruiken.

[ Voor 20% gewijzigd door TallManNL op 07-03-2011 11:42 ]

geheelonthouder met geheugenverlies


Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

430xlkod schreef op maandag 07 maart 2011 @ 10:56:
Maar in principe moet ik dan bijna voor elke 'verandering' in de UI die in de functie gebeurt een aparte sub gaan maken?
- een sub met een string als parameter voor het log te updaten
- een sub zonder parameter om progressbar.performstep te doen en label te refreshen
- een sub om die messagebox op te gooien...
- een sub om buttons de enablen/disablen (kan nog in één gedaan worden met een boolean)

Is dat dan niet helemaal omslachtig, of werk ik dan verkeerd?
Het idee is dat je GUI in 1 thread draait, en dat dat de enige thread is die de GUI objecten manipuleert. Alle andere threads communiceren onderling en met de GUI thread, maar niet door aan "private data" van die thread te gaan zitten.

Naast het "simpele" corss-thread invocation op de GUI doen voor elke change (veel boilerplate code schrijven) zijn er meerdere manieren om dit op te lossen, bijvoorbeeld:
je kan een gedeeld "GUI state" object hebben (met informatie zoals de progress in procenten), en je refresht je GUI elke seconde met de GUI thread door de informatie uit het "GUI state" object te lezen. vergeet niet te locken op de juiste plaatsen :)

Ander voorbeeld is door middel van berichten sturen naar de GUI thread dat een thread een task heeft afgerond (en de GUI thread kan dan het bericht interpreteren door de progressbar verder te zetten)

Op die manier hoeft er dus geen enkele thread een cross-thread invocation op de GUI te doen :)

[ Voor 3% gewijzigd door MLM op 07-03-2011 11:47 ]

-niks-


Acties:
  • 0 Henk 'm!

  • 430xlkod
  • Registratie: Januari 2007
  • Laatst online: 06-12-2024
Oké, ik probeer jullie info te begrijpen (en dat lukt min of meer) maar ik weet dan niet hoe dit allemaal te integreren hierin mijn probleem. Ik heb even om het wat makkelijker begrijpbaar te maken de hoofdzaak van code gekopieerd:
Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
        Try
            Dim min As Integer = CType(txtStart.Text, Integer)
            Dim max As Integer = CType(txtEinde.Text, Integer)
            pgrBarTotal.Minimum = min
            pgrBarTotal.Maximum = max
            If (txtBaseURL.Text = Nothing Or txtExtensionURL.Text = Nothing) Then
                updateLog(Now() + ": Geen url of extensie gegeven..." + vbCrLf)
                throwException("Vergeten url en/of extensie in te geven?")
            Else
                Dim href As String = txtBaseURL.SelectedItem
                Dim ext As String = txtExtensionURL.SelectedItem
                draad = New Thread(AddressOf Me.bereken)
                draad.Start()
            End If
        Catch ex As Exception
            updateLog(Now() + ": Geen start en/of eind waarde opgegeven..." + vbCrLf)
            throwException("U heeft geen minimum en/of maximum opgegeven!")
        End Try
    End Sub

    Private Sub updateUI(ByVal text As String)
        If Me.InvokeRequired Then
            Me.Invoke(New ParameterizedThreadStart(AddressOf updateUI))
        Else
            pgrBarTotal.PerformStep()
            lblVoortgang.Text = text.ToString
        End If
    End Sub

    Private Sub updateLog(ByVal text As String)
        If Me.InvokeRequired Then
            Me.Invoke(New ParameterizedThreadStart(AddressOf updateLog))
        Else
            txtLog.AppendText(text)
        End If
    End Sub

    Private Sub changeButtons(ByVal switch As Boolean)
        If Me.InvokeRequired Then
            Me.Invoke(New ParameterizedThreadStart(AddressOf changeButtons))
            'System.Reflection.TargetParameterCountException was unhandled...
        Else
            If (switch = False) Then
                btnStart.Enabled = False
                btnSave.Enabled = False
            ElseIf (switch = True) Then
                btnStart.Enabled = True
                btnSave.Enabled = True
            End If
        End If
    End Sub

    Private Sub throwException(ByVal text As String)
        If Me.InvokeRequired Then
            Me.Invoke(New ParameterizedThreadStart(AddressOf throwException))
        Else
            MessageBox.Show(text, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End If
    End Sub

    Private Sub bereken()
        'changeButtons(False)
        'Zodra deze oproept, krijg ik de error op regel 42...
        Dim min As Integer = CType(txtStart.Text, Integer)
        Dim max As Integer = CType(txtEinde.Text, Integer)
        Dim href As String = txtBaseURL.SelectedItem
        Dim ext As String = txtExtensionURL.SelectedItem
        'Voor 67 en 68 krijg ik cross-threading exception.
        'Ik weet niet hoe ik deze waardes nu uit mijn main thread krijg. Het gekke is, bij regel 65 en 66 krijg ik geen error!?!

        updateLog(Now() & ": Gestart..." & vbCrLf)

        For i = min To max Step 1
            Dim percentage As Int16 = ((i - min) / (max - min)) * 100
            Math.Round(percentage, 4)
            updateUI((i - min) & " van " & (max - min) & " | " & percentage.ToString & "%")

            Dim url As String = (href.ToString + i.ToString + ext.ToString)
            Dim request As HttpWebRequest = WebRequest.Create(url)
            Try
                Dim response As HttpWebResponse = request.GetResponse()
                If (response.StatusCode <> 0) Then
                    updateLog(Now() + ": " + url + " -- " + response.StatusCode.ToString() + vbCrLf)
                End If
            Catch ex As Exception
                '...
            End Try
        Next
        changeButtons(True)
        updateLog(Now() + ": Gestopt... | " + min.ToString() + " tot " + max.ToString() + vbCrLf)
    End Sub

    Private Sub txtStart_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles txtStart.TextChanged
        If (txtStart.Text <> Nothing) Then
            txtEinde.Text = CType(txtStart.Text, Integer) + 100000
        End If
    End Sub
End Class


Ik snap dus niet waarom hij dan wel, en dan niet een fout geeft...

Acties:
  • 0 Henk 'm!

  • TallManNL
  • Registratie: Oktober 2005
  • Laatst online: 18-05 13:52
Voor al je helper functies geldt dat je in je invoke je parameter aan je invoke vergeet mee te geven. Overigens ga je door invoke ipv begininvoke nog steeds staan te wachten met je workerthread op je mainthread.
Visual Basic .NET:
1
2
3
4
5
6
7
8
    Private Sub updateUI(ByVal text As String) 
        If Me.InvokeRequired Then 
            Me.Invoke(New ParameterizedThreadStart(AddressOf updateUI), text) 
        Else 
            pgrBarTotal.PerformStep() 
            lblVoortgang.Text = text.ToString 
        End If 
    End Sub 


Verder zijn je regels 11 en 12 vrij nutteloos, je gebruikt de waardes niet. Die zou je via een ParameterizedThreadStart aan je thread kunnen geven, dan ga je regels 67 en 68 niet meer nodig hebben

[ Voor 15% gewijzigd door TallManNL op 07-03-2011 12:38 ]

geheelonthouder met geheugenverlies


Acties:
  • 0 Henk 'm!

  • 430xlkod
  • Registratie: Januari 2007
  • Laatst online: 06-12-2024
Kortom, je wil als ik de functie bereken oproep daar 2 parameters meegeef.
Maar zover ik weet gaat dat niet (of mij lukt het niet).
Ik kan in geen geval hier achter bereken parameters meegeven:
Visual Basic:
1
2
3
4
Dim href As String = txtBaseURL.SelectedItem
Dim ext As String = txtExtensionURL.SelectedItem
draad = New Thread(AddressOf Me.bereken(href,ext))
draad.Start()


Als ik dit dan verander:
Visual Basic:
1
2
3
4
5
6
Private Sub bereken(ByVal href As String, ByVal ext As String)
changeButtons(False)
Dim min As Integer = CType(txtStart.Text, Integer)
Dim max As Integer = CType(txtEinde.Text, Integer)
'Dim href As String = txtBaseURL.SelectedItem = niet nodig omdat het als parameter binnenkomt dan...
'Dim ext As String = txtExtensionURL.SelectedItem = niet nodig omdat het als parameter binnenkomt dan...


Dat gaat, maar hoe start ik die thread en geef ik die parameters door?

Wat betreft die parameters bij updateLog en updateUI: klopt, was die even vergeten :) .

Maar wat ik me afvraag, waarom geeft ie geen error van cross-threading bij het ophalen van min en max, de 2 regels voor 67 en 68...?

Wat ik nu dus gedaan heb:
- Elke Invoke naar BeginInvoke gedaan.
- Parameters meegegeven aan de invoke (changeButtons werkt nu dus ook)
- ...

Als ik hem nu run blijft ie dus nog hangen op regel 67. Weet namelijk niet hoe ik dat moet oplossen met jou info (tot nu toe).

Misschien voorbeeld?

[ Voor 5% gewijzigd door 430xlkod op 07-03-2011 21:11 ]


Acties:
  • 0 Henk 'm!

  • TallManNL
  • Registratie: Oktober 2005
  • Laatst online: 18-05 13:52
Je kunt default slechts 1 object meegeven aan een thread, danwel direct aan Start danwel via een ParameterizedThreadStart. Je zou een class kunnen maken waarin je wat publics opneemt voor de parameters die je mee wil nemen, een instance van die class maken en die aan je draad.Start meegeven.

Je kunt ook je href en ext als classwide variabelen opnemen, setten voor het starten van de thread en uitlezen in de thread. (Zie code hieronder)
Reden waarom min en max wel gaan en href en ext niet is omdat min en max van de property Text afkomen en de href en ext van een SeletectedItem wat waarschijnlijk niet threadsafe is.

Visual Basic .NET:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Class Bla
  Private href as string
  Private ext as string

  Sub Start
     href=control.SelectedItem
     ext=control.SelectedItem
     draad.start()
  End Sub

  Sub DraadProc
    ' Use href en ext rechtstreeks
  End Sub
End Class

geheelonthouder met geheugenverlies


Acties:
  • 0 Henk 'm!

  • 430xlkod
  • Registratie: Januari 2007
  • Laatst online: 06-12-2024
Als ik dan nu eens doe:
Visual Basic:
1
2
3
Dim fullURL As String = txtBaseURL.SelectedItem.toString() +  txtExtensionURL.SelectedItem.toString()
draad = New Thread(AddressOf Me.bereken)
draad.Start(fullURL) 


En dan in de functie bereken die fullURL terug split.
Ik heb wel vernomen (kan verkeerd zijn) dat die parameter bij draad.Start(...) wordt meegegeven automatisch in een data array staat (hij zegt wel object maar...)?
En deze dus moet oproepen in de functie als {0}. Kortom, item 0 uit de data array (object).

Acties:
  • 0 Henk 'm!

  • TallManNL
  • Registratie: Oktober 2005
  • Laatst online: 18-05 13:52
Jouw optie zal ook werken.
Je parameter object wat je meegeeft kan overigens van alles zijn, een List (Of String) of een Dictionary.

Visual Basic .NET:
1
2
3
4
5
6
7
8
9
10
Dim params As New Dictionary(Of String, String)
params.Add("href", href)
params.Add("ext", ext)

draad.Start(params)

....

' In je thread
Dim href As String = params("href")


Ietwat leesbaarder later voor onderhoud, je key beschrijft wat je value is, bij een array heb je dat voordeel niet.

geheelonthouder met geheugenverlies


Acties:
  • 0 Henk 'm!

  • 430xlkod
  • Registratie: Januari 2007
  • Laatst online: 06-12-2024
Oh, dat is inderdaad wel handiger. Werkt manier als data arrays in PHP bij databases.
Ik ga het vandaag even uitwerken wanneer ik tijd heb en report terug als het werkt.
Bedankt alvast voor de hulp!

EDIT:
Het is gelukt. Wel nog even moeten zoeken want dit wou niet gaan.
Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
'Class variabele gemaakt:
Dim params As New Dictionary(Of String, String)

'Binnen btnStart:
params.Add("base_url", txtBaseURL.SelectedItem)
params.Add("extension", txtExtensionURL.SelectedItem)
draad = New Thread(AddressOf Me.bereken)
draad.Start(params)

'De functie bereken:
Private Sub bereken()
changeButtons(False)
Dim min As Integer = CType(txtStart.Text, Integer)
Dim max As Integer = CType(txtEinde.Text, Integer)
Dim href As String = params("base_url")
Dim ext As String = params("extension")


Gaf hij een error bij draad.Start(params) dat hij geen parameters aannam. Na even 5min kijken op google had ik er dit van gemaakt:
Visual Basic:
1
Private Sub bereken(ByVal niks As Object)


En nu draait hij vrolijk en wel, en hij reageert op alles!
Thanks gasten, vooral jij TallManNL!

[ Voor 72% gewijzigd door 430xlkod op 08-03-2011 11:23 ]


Acties:
  • 0 Henk 'm!

  • Ventieldopje
  • Registratie: December 2005
  • Laatst online: 01:00

Ventieldopje

I'm not your pal, mate!

Vanwege tijdgebrek heb ik niet alles gelezen, ook mijn Basic is wat roestig (C# programmeur). Ik heb ooit een artikeltje geschreven op mijn site hoe je makkelijk een form in een Thread kan starten zonder dat je niks meer kan met dat venster.

http://blog.ventieldopje....orm-in-a-seperate-thread/

Gelukkig werkt het nu wel voor je maar kan misschien handig zijn voor andere mensen ;)

www.maartendeboer.net
1D X | 5Ds | Zeiss Milvus 25, 50, 85 f/1.4 | Zeiss Otus 55 f/1.4 | Canon 200 f/1.8 | Canon 200 f/2 | Canon 300 f/2.8

Pagina: 1