C# 在不阻止UI执行的情况下等待几秒钟

C# 在不阻止UI执行的情况下等待几秒钟,c#,timer,wait,C#,Timer,Wait,我想在两条指令之间等待几秒钟,但不阻止执行 例如,Thread.Sleep(2000)它不好,因为它会阻止执行 我的想法是调用一个方法,然后等待X秒(例如20秒),等待事件的到来。在20秒结束时,我应该根据20秒内发生的情况执行一些操作。这是使用另一个线程的好例子: // Call some method this.Method(); Task.Factory.StartNew(() => { Thread.Sleep(20000); // Do things here

我想在两条指令之间等待几秒钟,但不阻止执行

例如,
Thread.Sleep(2000)
它不好,因为它会阻止执行


我的想法是调用一个方法,然后等待X秒(例如20秒),等待事件的到来。在20秒结束时,我应该根据20秒内发生的情况执行一些操作。

这是使用另一个线程的好例子:

// Call some method
this.Method();

Task.Factory.StartNew(() =>
{
    Thread.Sleep(20000);

    // Do things here.
    // NOTE: You may need to invoke this to your main thread depending on what you're doing
});
上述代码要求.NET 4.0或更高版本,否则请尝试:

ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
{
    Thread.Sleep(20000);

    // Do things here
}));

查看
System.Threading.Timer
类。我想这就是你要找的

似乎显示该类的操作与您尝试执行的操作非常相似(在特定时间后检查状态)

MSDN链接中提到的代码示例:

using System;
using System.Threading;

class TimerExample
{
    static void Main()
    {
        // Create an AutoResetEvent to signal the timeout threshold in the
        // timer callback has been reached.
        var autoEvent = new AutoResetEvent(false);
        
        var statusChecker = new StatusChecker(10);

        // Create a timer that invokes CheckStatus after one second, 
        // and every 1/4 second thereafter.
        Console.WriteLine("{0:h:mm:ss.fff} Creating timer.\n", 
                        DateTime.Now);
        var stateTimer = new Timer(statusChecker.CheckStatus, 
                                autoEvent, 1000, 250);

        // When autoEvent signals, change the period to every half second.
        autoEvent.WaitOne();
        stateTimer.Change(0, 500);
        Console.WriteLine("\nChanging period to .5 seconds.\n");

        // When autoEvent signals the second time, dispose of the timer.
        autoEvent.WaitOne();
        stateTimer.Dispose();
        Console.WriteLine("\nDestroying timer.");
    }
}

class StatusChecker
{
    private int invokeCount;
    private int  maxCount;

    public StatusChecker(int count)
    {
        invokeCount  = 0;
        maxCount = count;
    }

    // This method is called by the timer delegate.
    public void CheckStatus(Object stateInfo)
    {
        AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;
        Console.WriteLine("{0} Checking status {1,2}.", 
            DateTime.Now.ToString("h:mm:ss.fff"), 
            (++invokeCount).ToString());

        if(invokeCount == maxCount)
        {
            // Reset the counter and signal the waiting thread.
            invokeCount = 0;
            autoEvent.Set();
        }
    }
}
// The example displays output like the following:
//       11:59:54.202 Creating timer.
//       
//       11:59:55.217 Checking status  1.
//       11:59:55.466 Checking status  2.
//       11:59:55.716 Checking status  3.
//       11:59:55.968 Checking status  4.
//       11:59:56.218 Checking status  5.
//       11:59:56.470 Checking status  6.
//       11:59:56.722 Checking status  7.
//       11:59:56.972 Checking status  8.
//       11:59:57.223 Checking status  9.
//       11:59:57.473 Checking status 10.
//       
//       Changing period to .5 seconds.
//       
//       11:59:57.474 Checking status  1.
//       11:59:57.976 Checking status  2.
//       11:59:58.476 Checking status  3.
//       11:59:58.977 Checking status  4.
//       11:59:59.477 Checking status  5.
//       11:59:59.977 Checking status  6.
//       12:00:00.478 Checking status  7.
//       12:00:00.980 Checking status  8.
//       12:00:01.481 Checking status  9.
//       12:00:01.981 Checking status 10.
//       
//       Destroying timer.

我想你想要的是任务。延迟。这不会像Sleep那样阻塞线程,这意味着您可以使用异步编程模型使用单个线程来实现这一点

async Task PutTaskDelay()
{
    await Task.Delay(5000);
} 

private async void btnTaskDelay_Click(object sender, EventArgs e)
{
    await PutTaskDelay();
    MessageBox.Show("I am back");
}

我真的不建议您使用
Thread.Sleep(2000)
,因为有几个原因(描述了几个),但最重要的是,它在调试/测试时没有用处


我建议使用而不是
Thread.Sleep()
。计时器可以让您频繁地执行方法(如果必要的话),并且在测试中使用起来更容易!有一个很好的例子说明了如何在超链接后面使用计时器-只需将您的逻辑“2秒后发生的事情”直接放入
timer.appeased+=new-ElapsedEventHandler(OnTimedEvent)方法。

如果您不想阻塞内容,也不想使用多线程,以下是您的解决方案:

UI线程未被阻塞,计时器等待2秒后再执行操作

下面是来自上面链接的代码:

        // Create a timer with a two second interval.
    aTimer = new System.Timers.Timer(2000);
    // Hook up the Elapsed event for the timer. 
    aTimer.Elapsed += OnTimedEvent;
    aTimer.Enabled = true;

    Console.WriteLine("Press the Enter key to exit the program... ");
    Console.ReadLine();
    Console.WriteLine("Terminating the application...");
我使用:

private void WaitNSeconds(int segudos)
{
如果(segudos<1)返回;
DateTime _desired=DateTime.Now.AddSeconds(segudos);
while(DateTime.Now<\u所需){
System.Windows.Forms.Application.DoEvents();
}
}

如果您无法将环境升级到.NET 4.5以访问异步API和等待API,则Omar的解决方案是不错的*。这就是说,这里有一个重要的变化,应该作出,以避免表现不佳。在调用Application.DoEvents()之间应该添加一点延迟,以防止CPU过度使用。加入

Thread.Sleep(1);
在调用Application.DoEvents()之前,可以添加这样的延迟(1毫秒),并防止应用程序使用所有可用的cpu周期

private void WaitNSeconds(int seconds)
{
    if (seconds < 1) return;
    DateTime _desired = DateTime.Now.AddSeconds(seconds);
    while (DateTime.Now < _desired) {
         Thread.Sleep(1);
         System.Windows.Forms.Application.DoEvents();
    }
}
private void WaitNSeconds(整数秒)
{
如果(秒<1)返回;
DateTime _desired=DateTime.Now.AddSeconds(秒);
while(DateTime.Now<\u所需){
睡眠(1);
System.Windows.Forms.Application.DoEvents();
}
}

*有关使用Application.DoEvents()的潜在陷阱的更详细讨论,请参阅。

如果可能,首选方法应使用异步方式或第二个线程

如果这不可能或不需要,那么使用
DoEvents()
的任何实现都是一个坏主意,因为它可能会导致问题。这主要是关于Winforms中的DoEvents,但WPF中可能存在的陷阱是相同的

然后,可以使用在调度程序上放置具有所需睡眠时间的帧:

using System;
using System.Threading;
using System.Windows.Threading;

public static void NonBlockingSleep(int timeInMilliseconds)
{
    DispatcherFrame df = new DispatcherFrame();

    new Thread((ThreadStart)(() =>
    {
        Thread.Sleep(TimeSpan.FromMilliseconds(timeInMilliseconds));
        df.Continue = false;

    })).Start();

    Dispatcher.PushFrame(df);
}

嗨,这是我的建议

 .......
var t = Task.Run(async () => await Task.Delay(TimeSpan.FromSeconds(Consts.FiveHundred)).ConfigureAwait(false));
                //just to wait task is done
                t.Wait();

请记住使用“wait”,否则延迟运行时不会影响应用程序。在我的情况下,我需要这样做,因为我已将一个方法传递给等待的线程,这导致了锁定,因为metod是在GUI线程上运行的,线程代码有时会调用该方法

Task<string> myTask = Task.Run(() => {
    // Your code or method call
    return "Maybe you want to return other datatype, then just change it.";
});
// Some other code maybe...
while (true)
{
    myTask.Wait(10);
    Application.DoEvents();
    if (myTask.IsCompleted) break;
}
Task myTask=Task.Run(()=>{
//您的代码或方法调用
return“可能您想返回其他数据类型,然后只需更改它。”;
});
//可能还有其他代码。。。
while(true)
{
myTask.Wait(10);
Application.DoEvents();
如果(myTask.IsCompleted)中断;
}

我们能看看你的意思吗?这是非常广泛的。此外,您正在构建一种应用程序吗?控制台、WPF、Winforms、ASP.NET应用程序或其他东西?您是否总是希望等待事件发生X秒,或者如果事件发生,您是否可以在事件发生时立即执行某些操作,如果事件未在X秒内发生,则执行其他操作,例如,像超时?你能提供一个小样本吗?@Jeroenvanevel在我帖子的链接中有一个例子。我还缺少什么补充?@Jeroenvanevel您应该添加一个示例,这样即使链接无效,您的答案仍能提供一个值。另外,“查看foobar”本身的价值很小。我不会将这个答案标记为“非答案”,但一些用户已经这样做了。谢谢,它看起来很有趣。我只有一个问题,C#无法识别async和Wait。你知道为什么吗?它们是对.Net Framework的全新添加,是在Visual Studio 2012和2013上与4.5版一起添加的。如果有帮助的话,这里有一个nuget软件包。谢谢。我安装了Visual Studio Express 2013,它现在有async和Wait。但是当想要使用任务延迟的方法时,它是不存在的。唯一允许的方法是Equals、ReferenceEquals、WaitAll和WaitAny。。。你知道为什么吗?你甚至不需要
PutTaskDelay()
只要调用
wait Task.Delay(5000)directly@Black这是你的改进版。这一点是正确的。如果你想办法改进解决方案,可以发表评论让作者决定做出改变,也可以发表你的备选方案作为你自己的答案(酌情引用衍生作品)。将您的新版本编辑到其他人的答案中是不合适的。我只想在我自己的旧答案中添加一条注释,说明我更喜欢其他答案中提到的异步方法,因为它们更干净,实践效果更好。如果你没有艾尔
Task<string> myTask = Task.Run(() => {
    // Your code or method call
    return "Maybe you want to return other datatype, then just change it.";
});
// Some other code maybe...
while (true)
{
    myTask.Wait(10);
    Application.DoEvents();
    if (myTask.IsCompleted) break;
}