稍等一会儿而不阻塞主线程

36

我希望我的方法等待大约500毫秒,然后检查某个标志是否已更改。如何在不阻塞我的应用程序的情况下完成这个任务?


9
这个问题描述不够明确,我们需要知道“应用程序的其余部分”是什么——它运行在同一个线程上,还是不同的线程、不同的机器上?尽管如此,到目前为止几乎所有的答案都很危险。使用DoEvents或Thread.Sleep都是最糟糕的做法,这表明应用程序设计得很差,存在着可能导致严重错误的可重入性漏洞。Tudor的回答是最好的:使用定时器。 - Eric Lippert
5
另外,请考虑调查C# 5的Async CTP版本。 我们添加了控制流,使您可以非常轻松地延迟500毫秒并从上次中断的位置继续,而不会阻塞任何线程或启动新的消息循环。 - Eric Lippert
9个回答

49

您可以使用await Task.Delay(500);而无需像Sleep一样阻塞线程,并且比定时器需要的代码要少得多。


1
这需要.NET 4.5,但如果你必须使用.NET 4.0,你可以像这样使用https://dev59.com/ZnTYa4cB1Zd3GeqPwpkq#17717159。 - Deantwo
1
这也需要函数是异步的。所以,如果由于某种原因您不想那样做,@Sinjai创建的计时器函数也非常好用。 - Nandostyle

42

Thread.Sleep(500)会强制当前线程等待500毫秒。它可以工作,但如果整个应用程序在一个线程上运行,这不是你想要的。

在这种情况下,你需要使用一个Timer,如下所示:

using System.Timers;

void Main()
{
    Timer t = new Timer();
    t.Interval = 500; // In milliseconds
    t.AutoReset = false; // Stops it from repeating
    t.Elapsed += new ElapsedEventHandler(TimerElapsed);
    t.Start();
}

void TimerElapsed(object sender, ElapsedEventArgs e)
{
    Console.WriteLine("Hello, world!");
}

您可以将AutoReset设为true(或者根本不设置)以使计时器重复运行。

3
只需稍作更正,Interval属性的值是以毫秒为单位,而不是秒 链接 - Mickael V.
1
“current thread”非常有用。我错误地认为Thread.Sleep会导致主线程休眠。 - samad montazeri
2
我认为您对AutoReset的使用有些混淆了。将其设置为true或根本不进行设置会导致计时器重复执行。链接 - JochemKempe
@JochemKempe 已修复,虽然我不确定为什么你一年前没有这样做。只是留下这个评论,以避免混淆任何未来的谷歌搜索者。 - Sinjai

8

我不太理解这个问题。

如果你想在检查之前阻止,可以使用Thread.Sleep(500);

如果你想每x秒异步检查一次,可以使用Timer来每x毫秒执行处理程序。

这不会阻塞你当前的线程。


1
我认为这就是原帖作者所需要的。 - Otiel
1
如果是GUI应用程序,则最好使用特定于所使用GUI库的计时器(例如,在WPF的情况下使用DispatcherTimer)。 - svick
“每隔 x 秒检查一次”和“等待 x 秒然后检查”之间有区别。我相信 OP 想要后者。 - Josh Noe

7

如果所讨论的方法在不同于应用程序其他部分的线程上执行,则执行以下操作:

Thread.Sleep(500);

@user898569,根据定义,如果你想要“等待某事”,那么某事需要坐在那里“等待”。因此,不,你必须使用另一个线程,否则它会阻塞你的程序。参见Tudor的答案,了解一种触发事件的方法,该事件将在主线程上运行,但它不会“等待”,而只是一个周期性事件。 - Scott Chamberlain
有趣的问题。我建议你阅读一下.NET中的异步编程。这是一个很好的起点链接:http://msdn.microsoft.com/en-us/vstudio/gg316360 - Phil Klein

2
System.Threading.Thread.Sleep(500);

更新

这不会阻塞您的应用程序,只会阻塞运行您方法的线程。


2
我的比7秒钟还要长,因为我输入了命名空间。没能第一个完成 :( - danludwig

0

异步任务:

 var task = new Task (() => function_test()); task.Start();

public void function_test() { `Wait for 5000 miliseconds`   Task.Delay(5000);` }

0

使用计时器应该可以解决问题

如果需要使用线程,这里有一个示例

void Main()
{
    System.Threading.Thread check= new System.Threading.Thread(CheckMethod);
    check.Start();
}

private void CheckMethod()
{
     //Code
     Thread.Sleep(500);
}

0

最近我遇到了同样的问题,需要在不阻塞用户界面的情况下按计划运行一个操作。

这是我的解决方案:

private void Button_Click(object sender, RoutedEventArgs e)
{
    RunOnSchedule(interval, cancellationToken);
}

private void RunOnSchedule(int interval, CancellationToken cancellationToken)
{
    // Start the task you want to run on schedule
    TaskToRunOnSchedule(args);
    Task.Run(async () => 
    {
        // This loop checks if the task was requested to be cancelled every 1000 ms
        for (int x = 0; x < interval; x+=1000)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                break;
            }

            await Task.Delay(1000);
        }
    }).GetAwaiter().OnCompleted(() =>
    {
        // Once the task for delaying is completed, check once more if cancellation is requested, as you will reach this point regardless of if it was cancelled or not.
        if (!cancellationToken.IsCancellationRequested)
        {
            // Run this method again
            RunOnSchedule(interval, cancellationToken);
        }
    });
}

-3
在WinForms应用程序中,当我想要在主线程上等待而不阻塞应用程序时,通常会使用
private void Wait (double milliseconds)
{
    DateTime next = System.DateTime.Now.AddMilliseconds(milliseconds);
    while (next > System.DateTime.Now)
        Application.DoEvents();
}

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