【问题标题】:New Multi-threading not working新的多线程不起作用
【发布时间】:2014-04-30 21:53:12
【问题描述】:

我正在尝试创建一个线程,因此当我单击一个按钮时,它会从一个类中创建一个新的 PictureBox,这就是我已经走了多远,但屏幕上什么也没有出现。

Form1代码:

Public Class Form1

Private pgClass As New SecondUIClass

Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
    pgClass = New SecondUIClass
    pgClass.x += 100
    pgClass.thread()
End Sub
End Class

类代码:

Imports System.Threading

Public Class SecondUIClass
Public Const count As Integer = 1000
Public emeny(count - 1) As PictureBox
Public counter As Integer = 0
Public x As Integer = 0
Private trd As Thread

Public Sub thread()
    trd = New Thread(AddressOf NewUIThread)
    trd.SetApartmentState(ApartmentState.STA)
    trd.IsBackground = False
    trd.Start()
End Sub

Private Sub NewUIThread()
    emeny(counter) = New PictureBox
    emeny(counter).BackColor = Color.Red
    emeny(counter).Visible = True
    emeny(counter).Location = New System.Drawing.Point(x, 100)
    emeny(counter).Size = New System.Drawing.Size(10, 50)
    Form1.Controls.Add(emeny(counter))
    For z = 0 To 13
        emeny(counter).Location = New Point(emeny(counter).Location.X + 10, emeny(counter).Location.Y)
        Application.DoEvents()
        Threading.Thread.Sleep(100)
    Next
    counter += 1
End Sub
End Class

我之前在这里发布过类似的东西,但不同的是,图片框显示在屏幕上,但我试图让它们同时移动但它们不会移动,它们一次只移动一个.我之前问的问题是这个Multi threading classes not working correctly

【问题讨论】:

  • 您使用线程和 application.DoEvents() 完全走错了路。这可能会导致非常讨厌的问题。看看async/await吧。
  • 使用异步会解决我的问题吗? @user1937198
  • 我假设您正在使用多线程来实现它,以便用户可以交互并创建更多 pictureboxes 而原始的移动正确?

标签: vb.net multithreading picturebox


【解决方案1】:

我对这个答案做了一些假设,所以它可能不适合你开箱即用,但我认为它会让你在不使用任何 Thread.Sleep 电话的情况下走上正轨,因为我个人不喜欢故意放慢速度我的应用程序,但这确实是个人喜好。

所以对于我的示例,我只使用了一堆textboxes,因为我没有任何方便的图片可以摆弄。但基本上是为了让用户在移动发生时仍然可以与程序交互,我使用了一个由用户启动的后台工作线程,一旦启动它就会将textboxes 向下移动到用户告诉它停下来,否则它会碰到我编造的任意边界。所以理论上开始是你的应用程序中的空格键,我的停止是向集合中添加另一个控件。对于您的内容,您需要在添加任何内容之前以及在更新位置时锁定集合,但这取决于您的判断。

所以肉和土豆:

在表单设计器中,我有三个按钮,btnGo、btnStop 和 btnReset。下面的代码处理这些按钮上的点击事件,因此您需要先创建这些按钮才能工作。

Public Class Move_Test
    'Flag to tell the program whether to continue or to stop the textboxes where they are at that moment.
    Private blnStop As Boolean = False
    'Worker to do all the calculations in the background
    Private WithEvents bgWorker As System.ComponentModel.BackgroundWorker
    'Controls to be moved.
    Private lstTextBoxes As List(Of TextBox)
    'Dictionary to hold the y positions of the textboxes.
    Private dtnPositions As Dictionary(Of Integer, Integer)

    Public Sub New()
        ' Default code. Must be present for VB.NET forms when overwriting the default constructor.
        InitializeComponent()

        ' Here I instantiate all the pieces. The background worker to do the adjustments to the position collection, the list of textboxes to be placed and moved around the form
        ' and the dictionary of positions to be used by the background worker thread and UI thread to move the textboxes(because in VB.NET you can not adjust controls created on the UI thread from a background thread.
        bgWorker = New System.ComponentModel.BackgroundWorker()
        Me.lstTextBoxes = New List(Of TextBox)
        Me.dtnPositions = New Dictionary(Of Integer, Integer)
        For i As Integer = 0 To 10
            Dim t As New TextBox()
            t.Name = "txt" & i
            t.Text = "Textbox " & i
            'I used the tag to hold the ID of the textbox that coorelated to the correct position in the dictionary, 
            ' technically you could use the same position for all of them for this example but if you want to make the things move at different speeds 
            ' you will need to keep track of each individually and this would allow you to do it.
            t.Tag = i
            dtnPositions.Add(i, 10)
            'Dynamically position the controls on the form, I used 9 textboxes so i spaced them evenly across the form(divide by 10 to account for the width of the 9th text box).
            t.Location = New System.Drawing.Point(((Me.Size.Width / 10) * i) + 10, dtnPositions(i))
            Me.lstTextBoxes.Add(t)
        Next
        'This just adds the controls to the form dynamically
        For Each r In Me.lstTextBoxes
            Me.Controls.Add(r)
        Next
    End Sub

    Private Sub Move_Test_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Try
            'Don't need to do anything here. Placeholder
        Catch ex As Exception
            MessageBox.Show("Error: " & ex.Message)
        End Try
    End Sub

    Private Sub btnGo_Click(sender As Object, e As EventArgs) Handles btnGo.Click
        Try
            If Not bgWorker.IsBusy Then
                'User starts the movement.
                bgWorker.RunWorkerAsync()
            End If
        Catch ex As Exception
            MessageBox.Show("Error: " & ex.Message)
        End Try
    End Sub

    Private Sub btnReset_Click(sender As Object, e As EventArgs) Handles btnReset.Click
        Try
            'Reset the positions and everything else on the form for the next time through
            ' I don't set the blnStop value to true in here because it looked cooler to keep reseting the textboxes 
            ' and have them jump to the top of the form and keep scrolling on their own...
            For Each r In Me.lstTextBoxes
                r.Location = New System.Drawing.Point(r.Location.X, 10)
            Next
            For i As Integer = 0 To dtnPositions.Count - 1
                dtnPositions(i) = 10
            Next
        Catch ex As Exception
            MessageBox.Show("Error: " & ex.Message)
        End Try
    End Sub

    Private Sub bgWorker_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bgWorker.DoWork
        Try
            'This is where we do all the work.
            ' For this test app all its doing is scrolling through each value in the dictionary and incrementing the value
            ' You could make the dictionary hold a custom class and have them throttle themselves using variables on the class(or maybe travel at an angle?)
            For i As Integer = 0 To dtnPositions.Count - 1
                dtnPositions(i) += 1
            Next
        Catch ex As Exception
            blnStop = True
        End Try
    End Sub

    Private Sub bgWorker_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgWorker.RunWorkerCompleted
        Try
            'Once the background worker is done updating the positions this function scrolls through the textboxes and assigns them their new positions.
            ' We have to do it in this event because we don't have access to the textboxes on the backgroun thread.
            For Each r In Me.lstTextBoxes
                r.Location = New System.Drawing.Point(r.Location.X, dtnPositions(CInt(r.Tag)))
            Next
            'use linq to find any textboxes whose position is beyond the threshhold that signifies they are down far enough.
            ' I chose the number 100 arbitrarily but it could really be anything.
            Dim temp = From r In Me.lstTextBoxes Where r.Location.Y > (Me.Size.Height - 100)

            'If we found any textboxes beyond our threshold then we set the top boolean
            If temp IsNot Nothing AndAlso temp.Count > 0 Then
                Me.blnStop = True
            End If
            'If we don't want to stop yet we fire off the background worker again and let the code go otherwise we set the stop boolean to false without firing the background worker
            ' so we will be all set to reset and go again if the user clicks those buttons.
            If Not Me.blnStop Then
                bgWorker.RunWorkerAsync()
            Else
                Me.blnStop = False
            End If
        Catch ex As Exception
            MessageBox.Show("Error: " & ex.Message)
        End Try
    End Sub

    Private Sub btnStop_Click(sender As Object, e As EventArgs) Handles btnStop.Click
        Try
            'The user clicked the stop button so we set the boolean and let the bgWorker_RunWorkerCompleted handle the rest.
            Me.blnStop = True
        Catch ex As Exception
            MessageBox.Show("Error: " & ex.Message)
        End Try
    End Sub
End Class

那里有很多代码,但其中很多是 cmets,我试图尽可能清楚,所以它们可能有点啰嗦。但是您应该能够将该代码放在新表单上,并且无需任何更改即可工作。我的表格尺寸很大(1166 x 633)。所以我认为那是它最有效的时候,但任何尺寸都应该有效(较小的形式只会更混乱)。

如果这不适用于您的应用程序,请告诉我。

【讨论】:

  • 谢谢! @Mike_OBrien 它做了我想做的事情,但我想改变一件事,以便以后可以使用它,我希望能够通过单击一个按钮来创建textboxes,我可以轻松地做到这一点但是然后当我点击“btnGo”时,什么都没有发生?你知道这是为什么吗?
  • 忽略该评论实际上@Mike_OBrien 我想出了一个解决方法,每次单击按钮创建textboxes 时,我都会调用InitializeComponent()。我不太确定为什么每次都调用它会阻止textboxes 移动?
  • @user2387537 InitializeComponent() 是一个自动生成的函数,所以我并不完全肯定它在做什么,我从来没有真正需要去研究它。在大多数情况下,我认为它正在实例化控件并将它们添加到表单中,因此我认为当它尝试将多个具有相同名称的控件添加到表单controls 集合时,调用它两次会引发错误。这会导致您的 btnGo 处理程序在移动控件之前出错。
【解决方案2】:

这是一个非常适合 async/await 的问题。 Await 允许您暂停代码以在特定时间段内处理其他事件。

Private Async Function Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) As Task Handles Button1.Click
    pgClass = New SecondUIClass
    pgClass.x += 100
    await pgClass.NewUIThread()
End Sub
End Class

类代码:

Imports System.Threading

Public Class SecondUIClass
Public Const count As Integer = 1000
Public emeny(count - 1) As PictureBox
Public counter As Integer = 0
Public x As Integer = 0

Private Async Function NewUIThread() As Task
    emeny(counter) = New PictureBox
    emeny(counter).BackColor = Color.Red
    emeny(counter).Visible = True
    emeny(counter).Location = New System.Drawing.Point(x, 100)
    emeny(counter).Size = New System.Drawing.Size(10, 50)
    Form1.Controls.Add(emeny(counter))
    For z = 0 To 13
        emeny(counter).Location = New Point(emeny(counter).Location.X + 10, emeny(counter).Location.Y)
        await Task.Delay(100) 'The await state machine pauses your code here in a similar way to application.doevents() until the sleep has completed.
    Next
    counter += 1
End Sub
End Class

【讨论】:

  • await Threading.Thread.sleep(100) 给出错误“表达式不产生值”@user1937198
猜你喜欢
  • 1970-01-01
  • 2018-09-11
  • 1970-01-01
  • 1970-01-01
  • 2021-08-07
  • 1970-01-01
  • 1970-01-01
  • 2015-07-31
  • 1970-01-01
相关资源
最近更新 更多