dotNet:有没有办法在 UI 线程上执行 Join 语句? [英] dotNet: Is there a way to do a Join statement on the UI Thread?

查看:44
本文介绍了dotNet:有没有办法在 UI 线程上执行 Join 语句?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个简单的线程应用程序:单击开始按钮时,应用程序禁用此按钮,运行 5 个线程,只需进行迭代并更新 5 个 ProgressBar.最后一个线程正在等待线程结束,以重新启用我的开始按钮.

问题:用户在进度条达到 100% 之前看到按钮已启用......而且进度条仍在更新!

有什么问题吗?有没有办法在 UI 线程上做一个 join 语句?

代码如下:

导入 System.Threading公共结构线程参数公共 ThreadId 作为整数公共 pgBar 作为 ProgressBar公共迭代作为整数端部结构公共结构 SetPgValueParameters公共 pgBar 作为 ProgressBar公共值作为整数端部结构公开课表1私有线程(4) 作为线程私有共享 FormThread 作为线程Private Sub StartButton_Click(sender As Object, e As EventArgs) 处理 StartButton.ClickStartButton.Enabled = FalseDim MainThread As Thread = 新线程(AddressOf WaitThreads)主线程.开始()对于 i = 0 到 4线程(i) = 新线程(AddressOf ThreadRun)Dim params 作为 ThreadParametersparams.pgBar = Me.Controls.Find("ProgressBar" & (i + 1), False)(0)params.iterations = 100params.ThreadId = i线程(i).开始(参数)下一个结束子Private Sub ThreadRun(params As ThreadParameters)Dim invokeParams As SetPgValueParameters将 lastIntegerVal 调暗为 Integer = 1invokeParams.pgBar = params.pgBar对于 i = 0 到 params.iterationsinvokeParams.value = (100 * i/params.iterations)setPgValue(invokeParams)Console.WriteLine(params.ThreadId & ":" & i & "/" & params.iterations)下一个结束子Private Delegate Sub setPgValueDelegate(params As SetPgValueParameters)Private Sub setPgValue(params As SetPgValueParameters)如果 params.pgBar.InvokeRequired 那么Dim d As New setPgValueDelegate(AddressOf setPgValue)Me.Invoke(d, New Object() {params})别的params.pgBar.Value = params.value'Application.DoEvents()万一结束子私有子 WaitThreads()对于 i = 0 到 4线程(i).Join()下一个FormThread.Join()设置按钮启用()结束子私人委托子 setButtonEnabledDelegate()私有子 setButtonEnabled()如果 StartButton.InvokeRequired 那么Dim d As New setButtonEnabledDelegate(AddressOf setButtonEnabled)Me.Invoke(d,Nothing)别的Application.DoEvents()StartButton.Enabled = TrueConsole.WriteLine("Bouton réactivé")万一结束子结束类

<小时>

感谢 Bjørn-Roger Kringsjå 的链接.所以有一个解决方法:增加并直接减少值跳过动画.对于最大值,将其设置为最大值,减少1,增加1...仍然有一点延迟,但它确实比在 ProgressBars 位于中间时启用按钮要好 :)

解决方案

好的,我做了一些测试,结果是进度条动画的问题.当您的按钮被启用时(这是正确的),进度条仍处于动画模式".这可以通过禁用 XP 视觉样式.

进度条有一个属性名称 MarqueeAnimationSpeed,但不幸的是,这在使用 Blocks 样式时无效.

我进行了谷歌搜索并找到了这篇 SO 帖子:

更改值时禁用 .NET 进度条动画?>

示例应用

导入 System.Threading导入 System.Threading.Tasks公开课表1公共子新建()Me.InitializeComponent()Me.StartButton = New Button() With {.TabIndex = 0, .Dock = DockStyle.Top, .Text = "Start", .Height = 30}Me.ProgressBar1 = New ProgressBar with {.TabIndex = 1, .Dock = DockStyle.Top, .Height = 30, .Name = "ProgressBar1"}Me.ProgressBar2 = New ProgressBar with {.TabIndex = 2, .Dock = DockStyle.Top, .Height = 30, .Name = "ProgressBar2"}Me.ProgressBar3 = New ProgressBar with {.TabIndex = 3, .Dock = DockStyle.Top, .Height = 30, .Name = "ProgressBar3"}Me.ProgressBar4 = New ProgressBar with {.TabIndex = 4, .Dock = DockStyle.Top, .Height = 30, .Name = "ProgressBar4"}Me.ProgressBar5 = New ProgressBar with {.TabIndex = 5, .Dock = DockStyle.Top, .Height = 30, .Name = "ProgressBar5"}Me.Controls.AddRange({Me.ProgressBar5, Me.ProgressBar4, Me.ProgressBar3, Me.ProgressBar2, Me.ProgressBar1, Me.StartButton})结束子Private Sub HandleStartButtonClicked(sender As Object, e As EventArgs) 处理 StartButton.ClickMe.StartButton.Enabled = FalseDim tasks As Task() = New Task(4) {}对于 i 作为整数 = 0 到 4Dim index As Integer = i任务(i) = 新任务(子()Dim n As Integer = rnd.Next(100, 1001)对于 j 作为整数 = 0 到 n我.调用(子()DirectCast(Me.Controls("ProgressBar" & (index + 1).ToString()), ProgressBar).Value = CInt((j/n) * 100.0#)结束子)线程.睡眠(10)下一个结束子)下一个Task.Factory.ContinueWhenAll(任务,Sub() Me.Invoke(子()MessageBox.Show(String.Join(Environment.NewLine, From pb As ProgressBar In Me.Controls.OfType(Of ProgressBar)() Select (pb.Name & ": " & pb.Value.ToString())))Me.StartButton.Enabled = True结束子))Array.ForEach(Of Task)(tasks, Sub(t) t.Start())结束子私有 WithEvents 开始按钮作为按钮Private WithEvents ProgressBar1 作为 ProgressBarPrivate WithEvents ProgressBar2 作为 ProgressBarPrivate WithEvents ProgressBar3 作为 ProgressBarPrivate WithEvents ProgressBar4 作为 ProgressBarPrivate WithEvents ProgressBar5 作为 ProgressBar私有共享 rnd 作为新随机()结束类

I'm coding a simple thread application: when clicking a start button, the application disable this button, run 5 threads simply making For iterations and updating 5 ProgressBars. A last thread is waiting for the end of the threads, to re-enable my start button.

Problem: The user is seeing the button enabled before the progressbars are at 100%... And the progressbars are still updating!

What's the problem? Is there a way to make a join statement on the UI Thread?

Here is the code:

Imports System.Threading

Public Structure ThreadParameters
    Public ThreadId As Integer
    Public pgBar As ProgressBar
    Public iterations As Integer
End Structure

Public Structure SetPgValueParameters
    Public pgBar As ProgressBar
    Public value As Integer
End Structure

Public Class Form1

Private threads(4) As Thread
Private Shared FormThread As Thread

Private Sub StartButton_Click(sender As Object, e As EventArgs) Handles StartButton.Click
    StartButton.Enabled = False

    Dim MainThread As Thread = New Thread(AddressOf WaitThreads)
    MainThread.Start()

    For i = 0 To 4
        threads(i) = New Thread(AddressOf ThreadRun)

        Dim params As ThreadParameters

        params.pgBar = Me.Controls.Find("ProgressBar" & (i + 1), False)(0)
        params.iterations = 100
        params.ThreadId = i

        threads(i).Start(params)
    Next

End Sub

Private Sub ThreadRun(params As ThreadParameters)
    Dim invokeParams As SetPgValueParameters
    Dim lastIntegerVal As Integer = 1
    invokeParams.pgBar = params.pgBar
    For i = 0 To params.iterations
            invokeParams.value = (100 * i / params.iterations)
        setPgValue(invokeParams)
        Console.WriteLine(params.ThreadId & ":" & i & "/" & params.iterations)
    Next
End Sub

Private Delegate Sub setPgValueDelegate(params As SetPgValueParameters)
Private Sub setPgValue(params As SetPgValueParameters)
    If params.pgBar.InvokeRequired Then
        Dim d As New setPgValueDelegate(AddressOf setPgValue)
        Me.Invoke(d, New Object() {params})
    Else
        params.pgBar.Value = params.value
        'Application.DoEvents()
    End If
End Sub


Private Sub WaitThreads()
    For i = 0 To 4
        threads(i).Join()
    Next
    FormThread.Join()
    setButtonEnabled()
End Sub

Private Delegate Sub setButtonEnabledDelegate()
Private Sub setButtonEnabled()
    If StartButton.InvokeRequired Then
        Dim d As New setButtonEnabledDelegate(AddressOf setButtonEnabled)
        Me.Invoke(d, Nothing)
    Else
        Application.DoEvents()
        StartButton.Enabled = True
        Console.WriteLine("Bouton réactivé")
    End If
End Sub
End Class


EDIT: Thanks for the link Bjørn-Roger Kringsjå. So there's a work-around: increase and directly dicrease the value skip the animation. For the max value, set it the value to the max, decrease by 1, and increase by 1... There still a little lag, but it's really better than having the button enabled when ProgressBars are in the middle :)

解决方案

Okay, I did some testing and it turns out that the problem lies with the progress bar animation. At the time your button gets enabled (which is correct) the progress bar is still in "animation mode". This can be verified by disabling XP visual styles.

The progress bar have a property name MarqueeAnimationSpeed, but unfortunately this have no effect when using Blocks style.

I did a google search and found this SO post:

Disabling .NET progressbar animation when changing value?

Sample application

Imports System.Threading
Imports System.Threading.Tasks

Public Class Form1

    Public Sub New()
        Me.InitializeComponent()
        Me.StartButton = New Button() With {.TabIndex = 0, .Dock = DockStyle.Top, .Text = "Start", .Height = 30}
        Me.ProgressBar1 = New ProgressBar With {.TabIndex = 1, .Dock = DockStyle.Top, .Height = 30, .Name = "ProgressBar1"}
        Me.ProgressBar2 = New ProgressBar With {.TabIndex = 2, .Dock = DockStyle.Top, .Height = 30, .Name = "ProgressBar2"}
        Me.ProgressBar3 = New ProgressBar With {.TabIndex = 3, .Dock = DockStyle.Top, .Height = 30, .Name = "ProgressBar3"}
        Me.ProgressBar4 = New ProgressBar With {.TabIndex = 4, .Dock = DockStyle.Top, .Height = 30, .Name = "ProgressBar4"}
        Me.ProgressBar5 = New ProgressBar With {.TabIndex = 5, .Dock = DockStyle.Top, .Height = 30, .Name = "ProgressBar5"}
        Me.Controls.AddRange({Me.ProgressBar5, Me.ProgressBar4, Me.ProgressBar3, Me.ProgressBar2, Me.ProgressBar1, Me.StartButton})
    End Sub

    Private Sub HandleStartButtonClicked(sender As Object, e As EventArgs) Handles StartButton.Click

        Me.StartButton.Enabled = False

        Dim tasks As Task() = New Task(4) {}

        For i As Integer = 0 To 4
            Dim index As Integer = i
            tasks(i) = New Task(
                Sub()
                    Dim n As Integer = rnd.Next(100, 1001)
                    For j As Integer = 0 To n
                        Me.Invoke(
                            Sub()
                                DirectCast(Me.Controls("ProgressBar" & (index + 1).ToString()), ProgressBar).Value = CInt((j / n) * 100.0#)
                            End Sub
                        )
                        Thread.Sleep(10)
                    Next
                End Sub
                )
        Next

        Task.Factory.ContinueWhenAll(tasks,
            Sub() Me.Invoke(
                Sub()
                    MessageBox.Show(String.Join(Environment.NewLine, From pb As ProgressBar In Me.Controls.OfType(Of ProgressBar)() Select (pb.Name & ": " & pb.Value.ToString())))
                    Me.StartButton.Enabled = True
                End Sub
            )
        )

        Array.ForEach(Of Task)(tasks, Sub(t) t.Start())

    End Sub

    Private WithEvents StartButton As Button
    Private WithEvents ProgressBar1 As ProgressBar
    Private WithEvents ProgressBar2 As ProgressBar
    Private WithEvents ProgressBar3 As ProgressBar
    Private WithEvents ProgressBar4 As ProgressBar
    Private WithEvents ProgressBar5 As ProgressBar
    Private Shared rnd As New Random()

End Class

这篇关于dotNet:有没有办法在 UI 线程上执行 Join 语句?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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