新的多线程无法正常工作 [英] New Multi-threading not working

查看:127
本文介绍了新的多线程无法正常工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图创建一个线程,所以当我单击一个按钮时,它会从一个类创建一个新的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

我之前在此处发布过类似的内容,但有所不同,图片框显示在屏幕上,但是我试图让它们同时移动,但它们不会移动,一次只能移动一个.我之前问的问题是这个多线程类无法正常工作

解决方案

我对此回答做了一些假设,因此它可能对您不起作用,但我认为它将使您走上正确的轨道而无需使用任何方法Thread.Sleep之所以打电话,是因为我个人不喜欢为我的应用程序故意放慢速度,但这确实是个人喜好.

因此,在我的示例中,我只使用了一堆textboxes,因为我没有任何易于整理的图片.但基本上是为了获得它,以便用户在移动发生时仍可以与程序进行交互,我使用了一个由用户启动的后台工作线程,一旦启动,它将textboxes向下移动到窗体,直到用户告诉它为止停下来,或者碰到我所设定的任意边界.因此,从理论上讲,开始将是应用程序中的空格键,而我的出发点将是向集合中添加另一个控件.对于您的东西,您将需要在添加任何内容之前以及在更新职位时锁定收藏夹,但这取决于您的酌情决定权.

所以肉和土豆:

在表单的设计器中,我有三个按钮btnGo,btnStop和btnReset.下面的代码处理这些按钮上的click事件,因此您需要先创建这些按钮,然后才能起作用.

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

那里有很多代码,但其中很多是注释,我试图尽可能地清楚一些,以使它们可能有点冗长.但是您应该能够将该代码放入新的表单中,并且无需进行任何更改就可以正常工作.我的表格尺寸很大(1166 x 633).因此,我认为这是最好的方法,但是任何大小都应该起作用(较小的表单只会更混乱).

让我知道这是否不适用于您的应用程序.

I'm trying to create a thread so when I click a button it creates a new PictureBox from a class, this is how far I've got but nothing comes up on the screen at all.

Form1 code:

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

Class Code:

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

I have posted something similar before on here but it was different, the pictureBoxes were showing on the screen but I was trying to get them to move at the same time but they wouldn't move, they only moved one at a time. The question that I asked before was this Multi threading classes not working correctly

解决方案

I made a few assumptions for this answer so it may not work for you out of the box but I think it will put you on the right track without using any Thread.Sleep calls because I personally don't like building intentional slows to my apps but that's a personal preference really.

So For my example I just used a bunch of textboxes because I didn't have any pictures handy to fiddle with. But basically to get it so that the user can still interact with the program while the moving is happening I used a background worker thread that is started by the user and once its started it moves the textboxes down the form until the user tells it to stop or it hits an arbitrary boundary that I made up. So in theory the start would be the space bar in your app and my stop would be adding another control to the collection. For your stuff you will want to lock the collection before you add anything and while you are updating the positions but that is up to your discretion.

So the meat and potatoes:

in the designer of the form I had three buttons, btnGo, btnStop and btnReset. The code below handles the click event on those buttons so you will need to create those before this will work.

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

Theres a lot of code there but a lot of it is comments and I tried to be as clear as possible so they are probably a little long winded. But you should be able to plop that code on a new form and it would work without any changes. I had the form size quite large (1166 x 633). So I think that's when it works best but any size should work(smaller forms will just be more cluttered).

Let me know if this doesn't work for your application.

这篇关于新的多线程无法正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆