如何在x秒后触发一个方法运行?

15

我正在开发一款 C# Windows Forms 应用程序,在其中需要使方法暂停30秒,然后才能继续执行下一行代码。我尝试使用 Thread.Sleep() 方法,但它并不适合这个应用程序,因此我了解到应该使用某种计时器。我已经搜索了很多资料,但是我无法弄清楚如何实现计时器。

以下是我的代码,请问有人可以向我展示如何实现计时器吗?我在代码中做了一个注释,说明我希望这个方法暂停。

private void start_Vid_Click(object sender, EventArgs e)
{
    if (video.State != StateFlags.Running)
    {
        viewport.Visible = true;
        video.Play();
    }

    //Here I want to wait 30 seconds until the next line of code is triggered

    viewport.Visible = false;
    viewport2.Visible = true;
    pictureBox1.Visible = true;
    start_Webc(); 
    video2.Play();
}

3
创建一个 System.Windows.Forms.Timer - Alvin Wong
2
为什么在这个应用程序中不适合使用Thread.Sleep? - SergeyS
4
除非您明确在自己的线程上,否则Thread.Sleep很少是合适的。在这种情况下,它会完全阻塞UI,使界面无用并中断视频。 - poke
8个回答

34

如果您的应用程序是 .Net 4.5 应用程序,那么使用 Task 会更容易一些:

private async void start_Vid_Click(object sender, EventArgs e)
{
    if (video.State != StateFlags.Running)
    {
        viewport.Visible = true;
        video.Play();
    }

    await Task.Delay(TimeSpan.FromSeconds(30));

    viewport.Visible = false;
    viewport2.Visible = true;
    pictureBox1.Visible = true;
    start_Webc();
    video2.Play();
}

1
对于未来的访问者来说,这是一个很好的答案。+1 - Jamiec

14

在.NET 4.0上不支持Task.Delay,但是您可以启动一个任务,它只会休眠30秒,然后您可以再次在UI线程上继续:

Task.Factory.StartNew(() => Thread.Sleep(30 * 1000))
            .ContinueWith((t) =>
    {
         viewport.Visible = false;
         viewport2.Visible = true;
         pictureBox1.Visible = true;
         start_Webc(); 
         video2.Play();
    }, TaskScheduler.FromCurrentSynchronizationContext());

3
很棒的回答,我评论了。+1 - Jamiec
计时器也很好,因为Tick事件将在UI线程上执行。另外,如果OP代码不使用局部变量,那么您可以将计时器放在表单上,设置间隔,订阅事件。所有代码都像timer1.Start()一样简单。 - Sergey Berezovskiy
我想到了一个问题,这个解决方案中的所有变量(viewportpictureBox等)都是UI项,你可能需要对它们进行“Invoke”操作——因为“ContinueWith”会在自己的线程上执行,这可能会导致问题。 - Jamiec
@Jamiec 这就是为什么我继续使用 TaskScheduler.FromCurrentSynchronizationContext() :) - Sergey Berezovskiy
我今晚稍后会测试这个,但我有信心能让它正常工作!太棒了,有这么多人愿意帮助陌生人!如果没有像Stack Overflow这样的网站,像我这样的n00b程序员该怎么办呢 ;) - user1881847

7
System.Threading.Tasks.Task.Delay( 60 * 1000 ).ContinueWith( (_) => StartVoid() );

6

在类级别上定义一个Timer的实例(有几个类可以这样做 - 对于winforms应用程序,您应该使用System.Windows.Forms.Timer

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

然后在你的方法中添加行来处理它的tick并启动它:

private void start_Vid_Click(object sender, EventArgs e)
{
    if (video.State != StateFlags.Running)
    {
        viewport.Visible = true;
        video.Play();
    }

    myTimer.Tick += (o,ea) => {    
        viewport.Visible = false;
        viewport2.Visible = true;
        pictureBox1.Visible = true;
        start_Webc(); 
        video2.Play();
        myTimer.Stop();
    }
    myTimer.Interval = 5000; // 5 seconds
    myTimer.Start();
}

现在已经恢复了 :) 只是验证一下 .Net 4.0 上是否可用 Delay。 - Sergey Berezovskiy

4
我们可以使用来自的任务进行此操作。
System.Threading.Tasks.Task.Factory.StartNew(() =>
        {
            Thread.Sleep(5000);
            this.Invoke(new Action(() => 
                Method1()));
        });

3

使用响应式扩展库也可以实现:

Observable.Return("Delayed").Delay(TimeSpan.FromSeconds(30)).Subscribe(val => Console.WriteLine("Executing delayed"));

0

WPF案例..

  • WPF扩展:
public static partial class WpfUtils
{
    public static void RunAfter(double seconds, Action action)
    {
        if (Application.Current.Dispatcher == null)
            return;
        var myTimer = new DispatcherTimer(
            DispatcherPriority.Normal,
            Application.Current.Dispatcher)
        {
            Interval = TimeSpan.FromSeconds(seconds)
        };
        myTimer.Tick += (_, _) =>
        {
            action?.Invoke();
            myTimer.Stop();
        };
        myTimer.Start();
    }
}
  • 用法:
WpfUtils.RunAfter(3, () =>
{
    // some code
    Console.WriteLine("Running after 3 seconds");
});

请注意,此代码将在UI线程上运行。另一方面,如果您使用Task类中的方法,则延迟的代码将在后台线程上运行。
  • 示例:
Task.Delay(6 * 1000).ContinueWith(_ =>
{
    // this will run in a background-thread
    var b = Thread.CurrentThread.IsBackground;
    Debug.Assert(b);
    Console.WriteLine("3");
});

var timer = new DispatcherTimer
{
    Interval = TimeSpan.FromSeconds(3)
};
timer.Tick += (_, _) =>
{
    // this will run in the UI-thread
    var b = Thread.CurrentThread.IsBackground;
    Debug.Assert(!b);
    Console.WriteLine("2");
    timer.Stop();
};
timer.Start();

Console.WriteLine("1");

// output
// 1
// 2
// 3

0
我使用这种方法:
  1. 将一个Timer拖放到你的窗体上,并设置Enabled=FalseInterval=YOUR_DELAY
  2. 双击定时器,在其中放入你的函数。在最后一行添加Timer1.Enabled = false
  3. 当你想要启动延迟,比如在点击按钮之后,通过Timer1.Enabled = true来激活定时器
  4. 完成!
所以当你点击按钮或其他东西时,定时器会启动,在运行完你的代码后会再次被禁用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接