进度栏在阻止的 UI 线程中更新

本文关键字:UI 线程 更新 | 更新日期: 2023-09-27 17:57:13

为什么进度条会在理论上被阻止的UI线程中更新?

在简单的应用程序中,我有一个进度条和一个标签。我在 UI 线程中运行一个耗时的方法,该方法尝试更新进度条和标签。由于 UI 线程被阻止,这不应该起作用。但是进度条正在更新!

直到我在窗体上执行任何操作并且它冻结之前,进度条会更新(标签不会)。

为什么?

示例代码(在窗体上放置按钮、进度条和标签):

private void button1_Click(object sender, EventArgs e)
{
    while (true)
    {
        progressBar1.Value += 1;
        label1.Text += "1";
        Thread.Sleep(100);
    }
}

进度条正在更新,标签未更新。我的问题不是如何进行标签更新,而是为什么进度条正在更新。我确实知道线程,DoEvents,async/await,这不是答案。

进度栏在阻止的 UI 线程中更新

我认为

如果不拆卸一些Windows,就很难完全回答这个问题,这对我来说现在的工作太多了。

但基本上,当您设置 .WinForms 进度栏控件上的值,它只不过是使用消息 1026 (PBM_SETPOS) 调用 SendMessage,该消息告诉 Windows 进度栏设置其位置。

我的结论是,Windows 进度条会同步重绘自身以响应PBM_SETPOS以及响应WM_PAINT。 或者,也许它正在另一个线程上运行计时器以执行花哨的眩光动画,并且能够在不等待绘制消息的情况下重新绘制控件。

无论哪种方式,它只是您看到的Windows内部 - 并且在WM_PAINT处理程序之外绘制内容并不是一种不寻常的技术,即使它不是教科书式的做事方式。

实际上,查看PBM_SETPOS的文档(http://msdn.microsoft.com/en-us/library/bb760844(v=vs.85).aspx)它被记录为导致重绘 - 我想这是故意这样做的,以帮助懒惰/没有经验的人让阻塞进度条更新工作,而无需所有通常的麻烦。

有不同的方法可以回答您的问题。让我解释一下。如果您正在运行一个冗长的任务,用户必须等待才能继续,那么您是否在 UI 线程上运行实际上并不重要。无论哪种方式,用户都必须等待任务完成才能继续,因此他们无论如何都无法使用该应用程序。

但是,在您可能希望用户在任务运行时与应用程序的不同部分进行交互的操作中,您可以创建一个工作线程来执行所述任务。您需要了解主 UI 线程用于处理 UI。绘制进度条是 UI 的一部分。WinForms/.NET 的设计体系结构是,通过 BackgroundWorker 类创建后台线程,或者自行连接原始线程。它的设计使然。

但是,这一切都将在带有 async 和 await 关键字以及 Task 对象的 C# 5.0 中发生巨大变化。您可以在第9频道上搜索TechDays 11,或访问我的Facebook(在几篇帖子中发布了有关它的内容)。

如果您倾向于在任务中保留对 UI 线程调用 Application.DoEvents() 的操作以保持 Windows 消息的流动,您可以帮助纠正您的情况,或者您可以以正确的方式执行此操作并实际正确实现线程。使用 Thread 类、委托和调用非常容易连接,甚至更容易使用 BackgroundWorker ,它的设计目的几乎是在另一个线程上执行任务并报告 0/100% 进度值。