C# 如何异步等待 x 秒然后执行某些操作?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9112305/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 06:01:18  来源:igfitidea点击:

How to asynchronously wait for x seconds and execute something then?

c#winformsformssleepwait

提问by Dennis G

I know there is Thread.Sleepand System.Windows.Forms.Timerand Monitor.Waitin C# and Windows Forms. I just can't seem to be able to figure out how to wait for X seconds and then do something else - without locking the thread.

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

I have a form with a button. On button click a timer shall start and wait for 5 seconds. After these 5 seconds some other control on the form is colored green. When using Thread.Sleep, the whole application would become unresponsive for 5 seconds - so how do I just "do something after 5 seconds"?

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

采纳答案by eFloh

(transcribed from Ben as comment)

(转录自 Ben 作为评论)

just use System.Windows.Forms.Timer. Set the timer for 5 seconds, and handle the Tick event. When the event fires, do the thing.

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

...and disable the timer (IsEnabled=false) before doing your work in oder to suppress a second.

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

The Tick event maybe executed on another thread that cannot modify your gui, you can catch this:

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 */
                }
            }
        }
    }


Just for completeness: with async/await, one can delay execute something very easy (one shot, never repeat the invocation):

只是为了完整性:使用 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();
}

For more, see "async and await" in MSDN.

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

回答by mtijn

your application hangs because you are invoking the 5 second sleep/wait on the main UI thread. put the sleep/wait/whatever action in a separate thread (actually System.Windows.Forms.Timer should do that for you) and when it completes invoke the action that turns some control green. remember to check InvokeRequired. here's a short sample (SetText can be called from another thread, if it is the call will instead be invoked on the main UI thread where the textbox is on):

您的应用程序挂起是因为您在主 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;
}
}

I took the sample from here(well worth a read!).

我从这里拿了样本(非常值得一读!)。

回答by Ahmed Masud

I think what you are looking for is System.Timers... I don't think you need to set a CWND timer for what you are trying to do: Have a look at http://msdn.microsoft.com/en-us/library/0tcs6ww8(v=VS.90).aspxfor an example. It should do the trick.

我认为你正在寻找的是System.Timers......我认为你不需要为你想要做的事情设置一个 CWND 计时器:看看http://msdn.microsoft.com/en-us/library /0tcs6ww8(v=VS.90).aspx为例。它应该可以解决问题。

回答by PVitt

You can start an asynchronous task that performs your action:

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

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

[EDIT]

[编辑]

To pass the interval in you simply have to store it in a variable:

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

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

[/EDIT]

[/编辑]

回答by Tony Hopkinson

You are looking at it wrong. Click the button, it kicks off a timer with an interval of x seconds. When those are up it's eventhandler executes the task.

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

So what don't you want to happen.

所以你不想发生什么。

While the x seconds are elapsing.?

当 x 秒过去时。?

While The task is executing?

While 任务正在执行?

If for instance it's you don't want the button to be clicked until delay and task are done. Disable it in the button click handler, and enable it on task completion.

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

If all you want is a five second delay prior to the task, then you should pass the start delay to the task and let it take care of it.

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

回答by deegee

@eFloh in the post marked as answer said:

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

The Tick event may be executed on another thread that cannot modify your gui, you can catch this ...

Tick 事件可能会在另一个无法修改您的 gui 的线程上执行,您可以捕获这个...

That is not what the docs say.
You are using a System.Windows.Forms.Timer in your example code.
That is a Forms.Timer.
According to the C# docs the Timer events are raised on the UI thread.

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

This Windows timer is designed for a single-threaded environment where UI threads are used to perform processing. It requires that the user code have a UI message pump available and always operate from the same thread ...

此 Windows 计时器专为使用 UI 线程执行处理的单线程环境而设计。它要求用户代码有一个可用的 UI 消息泵,并且始终从同一线程运行......

Also see stackoverflow post here

另请参阅此处的stackoverflow帖子

回答by macio.Jun

Have you tried

你有没有尝试过

public static Task Delay(
    int millisecondsDelay
)

You can use like this:

你可以这样使用:

await Task.Delay(5000);

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

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

回答by Masuri

You can wait UI thread the way you want it to work.

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

Task.Factory.StartNew(async() =>
{
    await Task.Delay(2000);

    // it only works in WPF
    Application.Current.Dispatcher.Invoke(() =>
    {
        // Do something on the UI thread.
    });
});