【问题标题】:sleep not behaving as expected [duplicate]睡眠行为不如预期[重复]
【发布时间】:2013-12-17 20:56:25
【问题描述】:

我有一个 WPF 应用程序,我想在屏幕上显示某些信息之前先睡一秒钟。所以是。 1. 点击一个按钮。

  1. 睡眠 1 秒。
  2. 显示标签。
  3. 睡眠 1 秒。
  4. 显示标签。
  5. 等等等等

    private void RunTest()
    {
        clearTable();
    
        System.Threading.Thread.Sleep(1000);
    
        this.Cursor = Cursors.Wait;
    
        Label_OS_Result.Content = operatingSystem;
        Image_OS.Visibility = Visibility.Visible;
    
        System.Threading.Thread.Sleep(1000);
    
        Label_CPU_Result.Content = procName;
        Image_CPU.Visibility = Visibility.Visible;
    
        System.Threading.Thread.Sleep(1000);
     }
    

但是当我点击我的按钮时,它似乎会等待几秒钟,然后一次显示所有内容。

这是怎么回事??

【问题讨论】:

标签: c# .net wpf


【解决方案1】:

它正在按预期工作。

Thread.Sleep 在指定的时间内停止当前线程的执行。因此,当您更改图像的可见性时,它将进一步调用以更改框架中的可见性,然后它将尝试在屏幕上绘制它(无效)。当您在此操作完成之前使线程休眠时,它将没有足够的时间来完成此操作。您只是阻塞了负责在屏幕上绘制内容的 UI 线程。

注意Thread.Sleep 可能不是解决方案,你最好看看DispatcherTimer 或BackgroundWorkers。

【讨论】:

  • 正确。 Sleep 会阻塞你的 UI 线程。您将可以使用 .NET 4.5 的异步模式。你可以找到一个类似的例子here:Task.Factory.StartNew(() => Thread.Sleep(1000)) .ContinueWith((t) =>(...)
【解决方案2】:

您不想在显示您的 GUI 的线程上Thead.Sleep()

相反,您应该尝试await Task.Delay()。这类似于睡眠,但不会阻塞 GUI 线程。

这将允许所有渲染仍然发生,同时延迟标签的可见性更改。

【讨论】:

    【解决方案3】:

    它正在按预期工作。

    这可能不是您所期望的,但这里的问题是您的期望。

    当您在大多数可视控件上摆弄属性时会发生什么,最终会改变控件的外观,首先是对控件属性(即数据)进行内部调整,然后触发重绘。为了触发重绘,通常会将一条消息发布到拥有此可视控件的线程的消息队列中。

    此线程通常一次处理一条消息,无论是按钮单击、鼠标移动还是由于您更改图像而刚刚发布的绘制消息。

    但是,那个线程基本上是这样写的(伪代码):

    while (true)
    {
        var message = GetNextMessageFromQueue();
        ProcessMessage(message);
    }
    

    此循环上次处于活动状态时,它开始处理一条消息,该消息最终调用了您的代码,即现在正在休眠的代码。

    所以基本上,让消息泵(实现此循环的代码的典型名称)继续泵送消息,从而处理您的绘制消息,而不是休眠。

    您可能希望将处理分离到不同的线程,或其他一些“在后台”做事的异步方式。

    请注意,如果您开始执行任何类型的冗长处理以响应按钮单击或类似操作,而不是在后台线程(或其他异步方式)上执行,您将遇到同样的问题。 GUI 似乎已冻结。

    然而,这个问题非常普遍,以至于微软最终在 Windows 中构建了对它的特殊支持。过去,一扇窗户似乎只是挂着。然而,现在,如果消息队列“填满”,Windows 将淡出窗口并添加一个标题,表明它已停止响应。不是一个解决方案,但稍微好一点。

    【讨论】:

      【解决方案4】:

      睡眠功能正常。

      最好使用计时器来这样做,

          // Declare it in the constructor of the class
          int level = 0;
          Timer timer = new Timer
          timer.Interval = 1000;
          timer.Tick +=new EventHandler(timer_Tick);
      
      private void timer_Tick(object sender, EventArgs e)
      {
          timer.Stop();
          if (level == 0)
          {
              Label_OS_Result.Content = operatingSystem;
              Image_OS.Visibility = Visibility.Visible;
              ++level;
          }
          else if (level == 1)
          {
              Label_CPU_Result.Content = procName;
              Image_CPU.Visibility = Visibility.Visible;
              ++level;
          }
          else if (level > 1)
          {
              this.Cursor = Cursors.Default;
              return;
          }
          timer.Start();
      }
      private void RunTest()
      {
          clearTable();
      
          //System.Threading.Thread.Sleep(1000);
          timer.Start();
          this.Cursor = Cursors.Wait;
      }
      

      【讨论】:

      • 你试过运行这个吗?这将引发运行时异常,因为您无法从后台线程访问 UI 元素。计时器滴答事件在后台线程而不是 UI 线程上调用。
      • 是的,它工作正常,没有这样的例外。我没有在Thread中执行过Tick Event,所以你可以使用它。
      猜你喜欢
      • 2020-10-02
      • 2016-12-14
      • 2021-04-12
      • 1970-01-01
      • 1970-01-01
      • 2013-06-04
      • 1970-01-01
      • 2011-05-18
      相关资源
      最近更新 更多