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

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.

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

0

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

    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

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/

1

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

If Button1.InvokeRequired Then
   Button1.Invoke(Sub() Button1.Location = New Point(Button1.Location.X + o, Button1.Location.Y))

1 użytkowników online, w tym zalogowanych: 0, gości: 1