【问题标题】:How to asynchronously wait for x seconds and execute something then?如何异步等待 x 秒然后执行某些操作?
【发布时间】:2012-02-25 02:32:43
【问题描述】:

我知道 C# 和 Windows 窗体中有 Thread.SleepSystem.Windows.Forms.TimerMonitor.Wait。我似乎无法弄清楚如何等待 X 秒然后做其他事情 - 不锁定线程。

我有一个带有按钮的表单。单击按钮时,计时器将启动并等待 5 秒。在这 5 秒之后,表单上的一些其他控件将变为绿色。使用Thread.Sleep 时,整个应用程序将在 5 秒内无响应 - 那么我该如何“在 5 秒后执行某项操作”?

【问题讨论】:

标签: c# winforms forms sleep wait


【解决方案1】:

(从 Ben 作为评论转录)

只需使用 System.Windows.Forms.Timer。将定时器设置为 5 秒,并处理 Tick 事件。当事件触发时,做这件事。

...并在执行您的工作之前禁用计时器 (IsEnabled=false) 以抑制一秒钟。

Tick 事件可能在另一个无法修改你的 gui 的线程上执行,你可以抓住这个:

private System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();

    private void StartAsyncTimedWork()
    {
        myTimer.Interval = 5000;
        myTimer.Tick += new EventHandler(myTimer_Tick);
        myTimer.Start();
    }

    private void myTimer_Tick(object sender, EventArgs e)
    {
        if (this.InvokeRequired)
        {
            /* Not on UI thread, reenter there... */
            this.BeginInvoke(new EventHandler(myTimer_Tick), sender, e);
        }
        else
        {
            lock (myTimer)
            {
                /* only work when this is no reentry while we are already working */
                if (this.myTimer.Enabled)
                {
                    this.myTimer.Stop();
                    this.doMyDelayedWork();
                    this.myTimer.Start(); /* optionally restart for periodic work */
                }
            }
        }
    }

为了完整性:使用 async/await,可以延迟执行一些非常简单的事情(一次,从不重复调用):

private async Task delayedWork()
{
    await Task.Delay(5000);
    this.doMyDelayedWork();
}

//This could be a button click event handler or the like */
private void StartAsyncTimedWork()
{
    Task ignoredAwaitableResult = this.delayedWork();
}

有关详细信息,请参阅 MSDN 中的“异步和等待”。

【讨论】:

    【解决方案2】:

    你试过了吗

    public static Task Delay(
        int millisecondsDelay
    )
    

    你可以这样使用:

    await Task.Delay(5000);
    

    参考:https://msdn.microsoft.com/en-us/library/hh194873(v=vs.110).aspx

    【讨论】:

      【解决方案3】:

      您可以启动一个异步任务来执行您的操作:

      Task.Factory.StartNew(()=>
      {
          Thread.Sleep(5000);
          form.Invoke(new Action(()=>DoSomething()));
      });
      

      [编辑]

      要传递间隔,您只需将其存储在变量中:

      int interval = 5000;
      Task.Factory.StartNew(()=>
      {
          Thread.Sleep(interval);
          form.Invoke(new Action(()=>DoSomething()));
      });
      

      [/EDIT]

      【讨论】:

        【解决方案4】:

        您可以按照您希望的方式等待 UI 线程。

        Task.Factory.StartNew(async() =>
        {
            await Task.Delay(2000);
        
            // it only works in WPF
            Application.Current.Dispatcher.Invoke(() =>
            {
                // Do something on the UI thread.
            });
        });
        

        如果您使用的是 .Net Framework 4.5 或更高版本,您可以使用Task.Run 而不是Task.Factory.StartNew,如下所示。

        int millisecondsDelay = 2000;
        
        Task.Run(async() =>
        {
            await Task.Delay(millisecondsDelay);
        
            // it only works in WPF
            Application.Current.Dispatcher.Invoke(() =>
            {
                // Do something on the UI thread.
            });
        });
        

        【讨论】:

          【解决方案5】:

          你看错了。 单击该按钮,它会启动一个间隔为 x 秒的计时器。当它们启动时,它的事件处理程序会执行任务。

          那么你不想发生什么。

          当 x 秒过去了。?

          任务正在执行时?

          例如,如果您不希望在延迟和任务完成之前单击按钮。在按钮单击处理程序中禁用它,并在任务完成时启用它。

          如果您想要的只是在任务之前有 5 秒的延迟,那么您应该将启动延迟传递给任务并让它处理它。

          【讨论】:

            【解决方案6】:

            您的应用程序挂起,因为您正在主 UI 线程上调用 5 秒睡眠/等待。将 sleep/wait/whatever 操作放在单独的线程中(实际上 System.Windows.Forms.Timer 应该为您执行此操作),并在完成时调用将某些控件变为绿色的操作。记得检查 InvokeRequired。这是一个简短的示例(SetText 可以从另一个线程调用,如果是调用,则会在文本框所在的主 UI 线程上调用):

            private void SetText(string text)
            {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.textBox1.InvokeRequired)
            {    
                SetTextCallback d = new SetTextCallback(SetText);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.textBox1.Text = text;
            }
            }
            

            我从here 获取样本(非常值得一读!)。

            【讨论】:

              【解决方案7】:

              @eFloh 在标记为答案的帖子中说:

              Tick 事件可能在另一个无法修改的线程上执行 你的 gui,你可以抓住这个......

              文档不是这么说的。
              您在示例代码中使用了 System.Windows.Forms.Timer。
              那是一个Forms.Timer
              根据 C# 文档,Timer 事件在 UI 线程上引发。

              此 Windows 计时器专为单线程环境而设计,其中 UI 线程用于执行处理。它要求用户 代码有一个可用的 UI 消息泵,并且总是从同一个操作 线程...

              另见stackoverflow post here

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-10-13
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多