带有线程计时器的Async /await

本文关键字:Async await 计时器 线程 | 更新日期: 2023-09-27 18:06:54

我能够解决输入表单Stephen Cleary的问题。

我有一个Windows窗体应用程序,它有一个带有异步修饰符的方法。如果该方法在按钮的点击事件中被调用,它不会阻塞UI线程。然而,当我在计时器内调用它作为回调时,它会冻结UI。我不知道我做错了什么。请参阅下面我的代码。这只是一个用于演示的示例项目。

public Form1()
{
    InitializeComponent();            
}
private async void formCloseAsync()
{
    shutdown stf = new shutdown();
    stf.StartPosition = FormStartPosition.CenterScreen;
    stf.Show();
    var task = Task.Factory.StartNew(processClose);
    await task;
}
private void processClose()
{
    Thread.Sleep(5000);
    Environment.Exit(1);
}
private void simpleButtonAsync_Click(object sender, EventArgs e)
{
    formCloseAsync();
}        
private void _simpleButtonTimer_Click(object sender, EventArgs e)
{
    Timer _shutdownTimer = new Timer(delegate
    {
       formCloseAsync();
    }, null, 5000, Timeout.Infinite);
}

更新1:谢谢大家的宝贵意见。请参阅下面的更新代码

public Form1()
    {
        InitializeComponent();
    }
    private Timer _shutdownTimer;
    private void formCloseAsync()
    {
        shutdown stf = new shutdown();
        stf.StartPosition = FormStartPosition.CenterScreen;
        stf.Show();
        Task.Run(async ()=>
            { 
                await Task.Delay(5000);
                Environment.Exit(1);
            }
        );
    }
    private void simpleButtonAsync_Click(object sender, EventArgs e)
    {
        formCloseAsync();
    }
    private void _simpleButtonTimer_Click(object sender, EventArgs e)
    {
        _shutdownTimer = new Timer(async delegate
        {
            formCloseAsync();
        }, null, 0, Timeout.Infinite);
    }

然而,我仍然有同样的问题。在计时器回调内部调用Shutdown STF时被阻塞。主UI是可以的。这没有问题。我只是想关机(stf) GUI是响应时,从计时器回调创建。关机GUI有一个进度条。

更新3:

这是最后的代码

public partial class Form1 : Form
{
    readonly shutdown _stf = new shutdown();
    public Form1()
    {
        InitializeComponent();
    }
    private Timer _shutdownTimer;
    private async Task formCloseAsync()
    {
        try
        {
            BeginInvoke(new MethodInvoker(delegate
            {
                _stf.StartPosition = FormStartPosition.CenterScreen;
                _stf.Show();
            }));
            await Task.Run( async () =>
                            {
                                await Task.Delay(5000);
                            });
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
        finally
        {
            Environment.Exit(1);
        }
    }
    private  void simpleButtonAsync_Click(object sender, EventArgs e)
    {
        formCloseAsync();
    }
    private void _simpleButtonTimer_Click(object sender, EventArgs e)
    {
        _shutdownTimer = new Timer(async delegate
        {
            formCloseAsync();
        }, null, 0, Timeout.Infinite);
    }
    private async void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        e.Cancel = true;
         await formCloseAsync();
    }

带有线程计时器的Async /await

Task.Delay代替:

await Task.Delay(5000);

由于Thread.Sleep实际上阻塞了当前线程,因此不应该在异步方法中使用。

你可以在这里找到更多的信息:何时使用任务。延迟,何时使用Thread.Sleep?

但是,当我在计时器内调用它作为回调时,它会冻结UI。

你不能从后台线程访问UI元素(我猜stf.Show();正在显示一个对话框)。最简单的修复方法可能是将System.Threading.Timer替换为Windows.Forms.Timer

其他笔记:

  • 你不应该使用StartNew;使用Task.Run代替。请看我的博客文章StartNew是危险的。
  • 除了事件处理程序之外,你不应该使用async void。请参阅我的MSDN文章异步编程的最佳实践。