无法使用经过的秒表时间更新标签

Having trouble updating label with elapsed stopwatch time

我想每隔一段时间就用方法完成所用的时间来更新标签。我创建了一个秒表来获取经过的时间,并创建了一个 windows 表单计时器来通过 Tick 事件用经过的时间更新标签。我整理了一个简短的问题示例,请参阅下文。

using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;

namespace SOWinFormsTest
{
    public partial class Form1 : Form
    {
        private static Stopwatch watch = new Stopwatch();
        private static System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            timer.Interval = 1000;
            timer.Tick += timer_Tick;
            timer.Start();

            watch.Start();
            SomeMethod();
            watch.Stop();

        }
        private void timer_Tick(Object sender, EventArgs e)
        {
            label1.Text = GetTimeString(watch.Elapsed);
        }

        private string GetTimeString(TimeSpan elapsed)
        {
            string time = string.Empty;
            time = string.Format("{0:00}:{1:00}:{2:00}.{3:000}",
                elapsed.Hours,
                elapsed.Minutes,
                elapsed.Seconds,
                elapsed.Milliseconds);

            return time;
        }

        // Mimicking a method that takes 5 seconds to complete 
        // My actual code could take a lot longer
        private void SomeMethod()
        {
            Thread.Sleep(5000);           
        }
    }
}

当我 运行 这个程序并单击我的按钮时,标签仅更新一次,即在 5 秒后 SomeMethod() 完成。我希望标签随着经过的时间每秒更新一次。有谁知道为什么这不能按预期工作?这是一个多线程问题吗?我需要一个 BackgroundWorker 吗?还是我完全漏掉了一些东西?

您正在 主 GUI 线程 上执行 SomeMethod。这将停止消息泵送。 .NET WinForms 没有很好地隐藏 Windows 内部结构,因此这成为一个问题。

简而言之,GUI 线程在“无限循环”中工作并处理来自 Windows 的消息,其中包括任何 GUI 更新消息、计时器消息、用户输入等(消息可能不仅来自Windows,但这与此问题无关)。

此处提到了这一点,假设您使用的是 .NET Framework 4.8:Timer class

如果您想测量一个方法的执行时间,您必须 运行 它在单独的线程中,以免阻塞 GUI 线程并获得更准确的结果(因为主线程上的 GUI 处理不会把执行时间搞得一团糟)。