Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/jsf/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Vb.net 如何在后台线程中运行代码,并且仍然可以访问UI?_Vb.net_Multithreading_Windows 7_Crash_Focus - Fatal编程技术网

Vb.net 如何在后台线程中运行代码,并且仍然可以访问UI?

Vb.net 如何在后台线程中运行代码,并且仍然可以访问UI?,vb.net,multithreading,windows-7,crash,focus,Vb.net,Multithreading,Windows 7,Crash,Focus,我在visual studio中使用.net语言在windows 10上制作了一个文件搜索程序, 我的问题从form1开始,调用“dim frm2 as form2=new form2”, 显示新表单后,我在表单1上启动while循环,将数据输入表单2中的列表框: 1) form1调用form2并显示它 2) form1开始一个while循环 3) 在frm2中向listbox1馈送数据的while循环内部 现在一切都在windows 10上运行,while循环可以按需运行而不会出现任何问题,窗

我在visual studio中使用.net语言在windows 10上制作了一个文件搜索程序, 我的问题从form1开始,调用“
dim frm2 as form2=new form2
”, 显示新表单后,我在表单1上启动while循环,将数据输入表单2中的列表框:

1) form1调用form2并显示它

2) form1开始一个while循环

3) 在frm2中向listbox1馈送数据的while循环内部

现在一切都在windows 10上运行,while循环可以按需运行而不会出现任何问题,窗口可以松开焦点并重新获得焦点,而不会显示任何
“无响应…”MSG或白/黑屏幕..

但是,当我将软件带到运行windows 7的朋友计算机上时,安装所有必需的框架和visual studio本身,在调试模式下从.sln运行它,并在同一文件夹上执行相同的搜索结果如下:

1)while循环运行平稳,只要表单2没有散焦 (在windows 10上不会发生的事情)

2)当我点击屏幕上的任意位置时,软件会失去焦点什么 导致1)发生(黑屏\白屏\无响应等)

3)如果我等待循环所需的时间,并且不要单击其他任何地方 它保持平稳运行,更新标签,就像它应该使用的那样 找到的文件数量。。甚至以100%的成功率完成循环 (再次,除非我点击某个地方)

代码示例:

Sub ScanButtonInForm1()
    Dim frm2 As Form2 = New Form2
    frm2.Show()
    Dim AlreadyScanned As HashSet(Of String) = New HashSet(Of String)
    Dim stack As New Stack(Of String)
    stack.Push("...Directoy To Start The Search From...")
    Do While (stack.Count > 0)
        frm2.Label4.Text = "-- Mapping Files... -- Folders Left:" + stack.Count.ToString + " -- Files Found:" + frm2.ListBox1.Items.Count.ToString + " --"
        frm2.Label4.Refresh()
        Dim ScanDir As String = stack.Pop
        If AlreadyScanned.Add(ScanDir) Then
            Try
                Try
                    Try
                        Dim directoryName As String
                        For Each directoryName In System.IO.Directory.GetDirectories(ScanDir)
                            stack.Push(directoryName)
                            frm2.Label4.Text = "-- Mapping Files... -- Folders Left:" + stack.Count.ToString + " -- Files Found:" + frm2.ListBox1.Items.Count.ToString + " --"
                            frm2.Label4.Refresh()
                        Next
                        frm2.ListBox1.Items.AddRange(System.IO.Directory.GetFiles(ScanDir, "*.*", System.IO.SearchOption.AllDirectories))
                    Catch ex5 As UnauthorizedAccessException
                    End Try
                Catch ex2 As System.IO.PathTooLongException
                End Try
            Catch ex4 As System.IO.DirectoryNotFoundException
            End Try
        End If
    Loop
End Sub
我的结论很简单

1) windows 7不支持通过while循环进行实时ui(标签)更新 从按钮调用

2) windows 7可能支持新的 运行相同循环的线程

我认为,如果我在一个线程中运行所有代码,那么ui将保持响应

(顺便说一下,在windows 10中,UI没有响应,但我仍然可以看到 标签刷新,当形成松散焦点时,无任何崩溃。

所以我知道怎么做,但我也知道如果我这样做,线程将无法更新表单中的列表框或标签并刷新它

因此线程需要用数据更新外部文件,form2需要从文件中实时读取数据,但它会产生同样的问题吗?我不知道该怎么办。。可以使用一些帮助和提示谢谢

我必须承认,这个循环在Windows10上运行时没有响应用户界面,这意味着我不能点击任何按钮,但我可以 仍然可以看到标签刷新,但在Windows7上,一切都是一样的 除非我在某个地方单击,否则无论我在何处单击windows,循环都是无效的 撞车

im使用framework 4.6.2 developer


应用程序冻结的原因是您正在UI线程上执行所有工作。签出Async并等待。它在后台使用线程,但更易于管理。这里举一个例子:


应用程序冻结的原因是您正在UI线程上执行所有工作。签出Async并等待。它在后台使用线程,但更易于管理。这里举一个例子:


虽然我很高兴您找到了解决方案,但我建议不要使用
Application.DoEvents()
,因为这是一种不好的做法

请参阅此博客帖子:

简单地说,
Application.DoEvents()
是一个肮脏的解决方法,它使您的UI看起来很有响应性,因为它强制UI线程处理所有当前可用的窗口消息
WM_PAINT
就是其中的一条消息,这就是窗口重画的原因

然而,这有一些背后的原因。。。例如:

  • 如果在这个“后台”过程中关闭表单,很可能会抛出错误

  • 另一个反面是,如果通过单击按钮调用
    scanbutoninform1()
    方法,则可以再次单击该按钮(除非设置
    Enabled=False
    ),并再次启动该过程,这将我们带到另一个反面:

  • 启动的
    Application.DoEvents()
    -循环越多,占用UI线程的次数就越多,这将导致CPU使用率快速上升。由于每个循环都在同一个线程中运行,因此处理器无法在不同的内核或线程上调度工作,因此代码将始终在一个内核上运行,消耗尽可能多的CPU

当然,替代品是适当的多线程(或者任务并行库,以您喜欢的为准)。常规多线程实际上并不难实现


基础 要创建新线程,只需声明的实例并将委托传递给希望线程运行的方法:

Dim myThread As New Thread(AddressOf <your method here>)

访问UI线程 关于多线程的棘手部分是封送对UI线程的调用。后台线程通常无法访问UI线程上的元素(控件),因为这可能会导致并发问题(两个线程同时访问同一个控件)。因此,您必须通过安排调用在UI线程本身上执行来封送对UI的调用。这样,您将不再有并发风险,因为所有与UI相关的代码都在UI线程上运行

要对UI线程进行marhsal调用,请使用或方法之一
BeginInvoke()
是异步版本,这意味着它不需要等待UI调用完成,就可以让后台线程继续工作

人们还应该确保检查,它告诉你,如果你已经在第
Dim myThread As New Thread(AddressOf myThreadMethod)
myThread.IsBackground = True
myThread.Start()
'This delegate will be used to tell Control.Invoke() which method we want to invoke on the UI thread.
Private Delegate Sub UpdateTextBoxDelegate(ByVal TargetTextBox As TextBox, ByVal Text As String)

Private Sub myThreadMethod() 'The method that our thread runs.
    'Do some background stuff...

    If Me.InvokeRequired = True Then '"Me" being the current form.
        Me.Invoke(New UpdateTextBoxDelegate(AddressOf UpdateTextBox), TextBox1, "Status update!") 'We are in a background thread, therefore we must invoke.
    Else
        UpdateTextBox(TextBox1, "Status update!") 'We are on the UI thread, no invoking required.
    End If

    'Do some more background stuff...
End Sub

'This is the method that Control.Invoke() will execute.
Private Sub UpdateTextBox(ByVal TargetTextBox As TextBox, ByVal Text As String)
    TargetTextBox.Text = Text
End Sub
Private Sub myThreadMethod()
    'Do some background stuff...

    If Me.InvokeRequired = True Then '"Me" being the current form.
        Me.Invoke(Sub() TextBox1.Text = "Status update!") 'We are in a background thread, therefore we must invoke.
    Else
        TextBox1.Text = "Status update!" 'We are on the UI thread, no invoking required.
    End If

    'Do some more background stuff...
End Sub
If Me.InvokeRequired = True Then
    Me.Invoke(Sub()
                  TextBox1.Text = "Status update!"
                  Me.Text = "Hello world!"
                  Label1.Location = New Point(128, 32)
                  ProgressBar1.Value += 1
              End Sub)
Else
    TextBox1.Text = "Status update!"
    Me.Text = "Hello world!"
    Label1.Location = New Point(128, 32)
    ProgressBar1.Value += 1
End If
Imports System.Runtime.CompilerServices

Public Module Extensions
    ''' <summary>
    ''' Invokes the specified method on the calling control's thread (if necessary, otherwise on the current thread).
    ''' </summary>
    ''' <param name="Control">The control which's thread to invoke the method at.</param>
    ''' <param name="Method">The method to invoke.</param>
    ''' <param name="Parameters">The parameters to pass to the method (optional).</param>
    ''' <remarks></remarks>
    <Extension()> _
    Public Function InvokeIfRequired(ByVal Control As Control, ByVal Method As [Delegate], ByVal ParamArray Parameters As Object()) As Object
        If Parameters IsNot Nothing AndAlso _
            Parameters.Length = 0 Then Parameters = Nothing

        If Control.InvokeRequired = True Then
            Return Control.Invoke(Method, Parameters)
        Else
            Return Method.DynamicInvoke(Parameters)
        End If
    End Function
End Module
Private Sub myThreadMethod()
    'Do some background stuff...

    Me.InvokeIfRequired(Sub()
                            TextBox1.Text = "Status update!"
                            Me.Text = "Hello world!"
                            Label1.Location = New Point(128, 32)
                        End Sub)

    'Do some more background stuff...
End Sub
Dim LabelWidth As Integer = Me.InvokeIfRequired(Function() Label1.Width)
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    Dim CounterThread As New Thread(AddressOf CounterThreadMethod)
    CounterThread.IsBackground = True
    CounterThread.Start()

    Button1.Enabled = False 'Make the button unclickable (so that we cannot start yet another thread).
End Sub

Private Sub CounterThreadMethod()
    Dim Time As Integer = 0

    While True
        Thread.Sleep(1000) 'Wait for approximately 1000 ms (1 second).
        Time += 1

        Me.InvokeIfRequired(Sub() Label1.Text = "Thread has been running for: " & Time & " seconds.")
    End While
End Sub