winforms 如何异步等待x秒然后执行某个操作?

u5i3ibmn  于 2022-12-23  发布在  其他
关注(0)|答案(7)|浏览(244)

我知道在C#和Windows窗体中有Thread.SleepSystem.Windows.Forms.TimerMonitor.Wait,但我似乎无法弄清楚如何等待X秒,然后做其他事情-而不锁定线程。
我有一个带有按钮的表单。单击按钮时,计时器将启动并等待5秒。5秒后,表单上的其他控件将变为绿色。当使用Thread.Sleep时,整个应用程序将在5秒内无响应-那么我如何“5秒后执行某项操作”呢?

anhgbhbe

anhgbhbe1#

(转抄自Ben作为评论)
只需使用System. Windows. Forms. Timer。2将计时器设置为5秒,并处理Tick事件。3当事件触发时,执行该操作。
...并在执行工作之前禁用计时器(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中的“异步和等待”。
更完整:根据您的框架,您很有可能拥有可以在内部处理调用的DispatcherTimer类(WPF变体)。

iaqfqrcu

iaqfqrcu2#

你有没有试

public static Task Delay(
    int millisecondsDelay
)

您可以像这样使用:

await Task.Delay(5000);

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

xeufq47z

xeufq47z3#

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

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()));
});
  • [编辑]*
aiqt4smr

aiqt4smr4#

您可以等待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.
    });
});
chy5wohz

chy5wohz5#

你看错了。点击按钮,它会启动一个间隔为x秒的计时器。当时间到了,eventhandler就会执行任务。
你不想发生什么。
当x秒过去时。
当任务正在执行时?
例如,如果你不希望在延迟和任务完成之前按钮被点击,那么在按钮点击处理程序中禁用它,并在任务完成时启用它。
如果您所需要的只是在任务开始前延迟5秒,那么您应该将开始延迟传递给任务,让它来处理。

wxclj1h5

wxclj1h56#

你的应用程序挂起因为你是调用这5秒睡眠/等待在这主要UI线程.把这睡眠/等待/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中获取了这个示例(非常值得一读!)。

zyfwsgd6

zyfwsgd67#

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

Tick事件可能在另一个线程上执行,该线程无法修改您的gui,您可以捕获此...
医生可不是这么说的。
您在示例代码中使用的是System.Windows.Forms.Timer。
这是一个 Forms.Timer
根据C#文档,计时器事件在UI线程上引发。
此Windows计时器是为单线程环境设计的,其中UI线程用于执行处理。它要求用户代码具有可用的UI消息泵,并且始终从同一线程操作...
另请参见stackoverflow post here

相关问题