VB.NET - Dlaczego wątki nie pracują równocześnie?

VB.NET - Dlaczego wątki nie pracują równocześnie?
CO
  • Rejestracja:około 9 lat
  • Ostatnio:około 9 lat
  • Postów:31
0

Witam,
dlaczego w tym prostym kodzie wątki nie pracują równocześnie?
@some_ONE podpowiedział że w aplikacji źle wykonuję Invoke, gdyż każdy z wątków wykonuje dalsze operacje w btn1_move zamiast w wątku to w aplikacji głównej (po else).
Chciałbym wiedzieć, w jaki sposób mogę ingerować z poziomu wątku w buttony / textboxy znajdujące się na Form1 ?
Jak widać na screenie , buttony przemieszczają się w różnych odstępkach czasowych zamiast jednocześnie.

Kopiuj
Imports System.Threading
Public Delegate Function Pomocnik(o As Integer)
Public Class Form1

    Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
        'TU URUCHAM WĄTKI
        For i As Integer = 1 To 100
            Dim T1 As New Thread(New ThreadStart(Sub() btn1_move(i)))
            T1.Start()
            Dim T2 As New Thread(New ThreadStart(Sub() btn2_move(i)))
            T2.Start()
            Dim T3 As New Thread(New ThreadStart(Sub() btn3_move(i)))
            T3.Start()
        Next
    End Sub

    'FUNKCJA ZMIENIA POZYCJE BUTTONA
    Public Function btn1_move(o As Integer)
        Thread.Sleep(500)
        If InvokeRequired Then
            Me.Invoke(New Pomocnik(AddressOf btn1_move), o)
        Else
            Button1.Location = New Point(Button1.Location.X + o, Button1.Location.Y)
        End If
    End Function

    'FUNKCJA ZMIENIA POZYCJE BUTTONA
    Public Function btn2_move(o As Integer)
        Thread.Sleep(500)
        If InvokeRequired Then
            Me.Invoke(New Pomocnik(AddressOf btn2_move), o)
        Else
            Button2.Location = New Point(Button2.Location.X + o, Button2.Location.Y)
        End If
    End Function

    'FUNKCJA ZMIENIA POZYCJE BUTTONA
    Public Function btn3_move(o As Integer)
        Thread.Sleep(500)
        If InvokeRequired Then
            Me.Invoke(New Pomocnik(AddressOf btn3_move), o)
        Else
            Button3.Location = New Point(Button3.Location.X + o, Button3.Location.Y)
        End If
    End Function
End Class

user image

edytowany 1x, ostatnio: Czarny Orzeł
SO
  • Rejestracja:ponad 10 lat
  • Ostatnio:około rok
0

Bo dalej programujesz metodą prób i błędów nie mając pojęcia co robisz...

Kopiuj
    Public Function btn1_move(o As Integer)
        If InvokeRequired Then
            Me.Invoke(New Pomocnik(AddressOf btn1_move), o)
        Else
            Button1.Location = New Point(Button1.Location.X + o, Button1.Location.Y)
        End If
        Thread.Sleep(500)
    End Function

Chcesz, żeby to się wykonywało na innym wątku, no i początkowo tak jest... W zasadzie to 2 pierwsze linijki działają na innym wątku.
Wchodzisz do metody i sprawdzasz InvokeRequired, oczywiście to jest true bo jesteś w innym wątku niż ten z którego kontrolka została utworzona, więc wywołujesz Invoke(...), który wywołuje tą samą metodę tylko, że na wątku UI.
Teraz InvokeRequired jest false więc przesuwasz przycisk i... każesz wątkowi UI czekać 500ms, więc Invoke z innego wątku będzie czekać te pół sekundy zanim przesunie kolejny przycisk.
Usuń to bezsensowne Thread.Sleep to będziesz miał wrażenie, że przyciski przesuwają się równocześnie(oczywiście, to nie jest możliwe, bo przesunięcie kontrolki musi nastąpić z wątku w którym została utworzona).

I jeszcze w pętli tworzysz 300 wątków za pomocą new Thread() :D Poczytasz w końcu o TPL czy nie? https://msdn.microsoft.com/pl-pl/library/dd537609(v=vs.110).aspx

Kopiuj
For i As Integer = 1 To 100
  Dim T1 As New Thread(New ThreadStart(Sub() btn1_move(i)))

To przekazywanie i do lambdy też będzie działało inaczej niż myślisz, pisał ci już o tym jakiś anonim w innym temacie, ale widzę, że postanowiłeś to zignorować :D
https://blogs.msdn.microsoft.com/ericlippert/2009/11/12/closing-over-the-loop-variable-considered-harmful/

CO
aaa dopiero zrozumiałem to co mi piszesz... czyli nieprawidłowo te Invoke wykonuję.. No ale jak inaczej mam uzyskać dostęp do buttona skoro przynależy on do Form1, z poziomu nowego wątku nie da się ingerować z buttony Form1
SO
Dostęp do buttona tak jak masz, chociaż można to zrobić prościej bez delegata, ale to szczegół. Problemem tutaj jest Thread.Sleep wywoływane na wątku UI.
CO
"można to zrobić prościej bez delegata" - co masz na myśli?
SO
  • Rejestracja:ponad 10 lat
  • Ostatnio:około rok
1

"można to zrobić prościej bez delegata" - co masz na myśli?

Kopiuj
If Button1.InvokeRequired Then
   Button1.Invoke(Sub() Button1.Location = New Point(Button1.Location.X + o, Button1.Location.Y))
CO
chyba działa ! :D Dziękuję bardzooo ! Jeszcze popatrzę na tę drugą uwagę z Threads, dziekuję !
CO
choć nadal nie wiem jak tę metodą można pobierać np wartości textboxów z Form1, no ale to już będę kombinować wg tego przykładu :)

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.