【问题标题】:Run function every few seconds in background without blocking UI c#在后台每隔几秒运行一次函数而不会阻塞 UI c#
【发布时间】:2016-09-21 22:11:29
【问题描述】:

如何在不阻塞 UI 的情况下使用任务异步等待每隔几秒运行一次任务。下面是我使用计时器的示例。我能够每隔几秒钟运行一次函数,但它阻塞了 UI。有人可以帮助我吗?我是异步编程的新手。

 private void Form1_Load(object sender, EventArgs e)
        {
            gridControl1.DataSource = GetData();
            timer.Interval = 5000;
            timer.Tick += new EventHandler(MyTimer_Tick);
            timer.Start();
        }


        private void MyTimer_Tick(object sender, EventArgs e)
        {
           gridControl1.DataSource = GetData();
           gridControl1.RefreshDataSource();
        }

        DataTable GetData()
        {
            DataTable dt = new DataTable();
            dt.Columns.Add("Value1", typeof(int));
            dt.Columns.Add("Value2", typeof(int));
            dt.Columns.Add("Value3", typeof(int));
            dt.Columns.Add("Value4", typeof(int));
            dt.Columns.Add("Value5", typeof(int));
            dt.Columns.Add("Value6", typeof(int));
            dt.Columns.Add("Value7", typeof(int));
            dt.Columns.Add("Value8", typeof(int));
            dt.Columns.Add("Value9", typeof(int));
            dt.Columns.Add("Value10", typeof(int));
            dt.Columns.Add("Value11", typeof(int));
            dt.Columns.Add("Value12", typeof(int));

            Random rnd = new Random();
            for (int i = 1; i < 1000000; i++)
            {
                dt.Rows.Add(rnd.Next(1, 100), rnd.Next(1, 100), rnd.Next(1, 100),
                    rnd.Next(1, 100), rnd.Next(1, 100), rnd.Next(1, 100), rnd.Next(1, 100), rnd.Next(1, 100), rnd.Next(1, 100),
                    rnd.Next(1, 100), rnd.Next(1, 100), rnd.Next(1, 100));
            }
            return dt;
        }

【问题讨论】:

  • 您使用的是什么版本的 C#?
  • @RJProgrammer - 我使用的是 4.6 版

标签: c# async-await task


【解决方案1】:

看起来您正在使用System.Forms.Timer,它会在 UI 线程上引发其事件。因此,您需要将任何长时间阻塞的工作推送到后台线程:

private async void MyTimer_Tick(object sender, EventArgs e)
{
 gridControl1.DataSource = await Task.Run(() => GetData());
 gridControl1.RefreshDataSource();
}

【讨论】:

  • @user 我的建议和斯蒂芬建议的有什么区别,让你说效果更好。我提出了完全相同的建议,只是将其重构为创建 GetDataAsync
  • @MrinalKamboj:您的解决方案在(假)异步方法的实现中使用了Task.Run。我的解决方案使用Task.Run调用 同步方法。更多信息here.
  • @StephenCleary,感谢您的澄清,以后会小心的
【解决方案2】:

您需要首先将 GetData 定义为异步方法,方法是更改​​返回类型:

Task<DataTable> GetData(){...}

为了匹配指南,您可能希望将 async 添加到方法名称

Task<DataTable> GetDataAsync(){...}

那么您可能希望通过 Task.Factory.StartNew 覆盖 GetDataAsync 方法中的所有代码

Task<DataTable> GetData(){
  return Task.Factory.StartNew(() =>
  {
     ...
      return dt;
   });
}

编辑: 正如@MrinalKamboj 所建议的那样 使用 Task.Run Insted

Task<DataTable> GetData(){
string result = await Task.Run(() =>
        {
            ...
           return dt;

        });

    return result;
}

对于重复任务,您可能希望摆脱计时器并查看以下答案: RepeatActionEvery Method

希望有帮助。

【讨论】:

  • 解决方案针对控制台,不适用于 Winforms,会导致调用死锁Wait
  • @MrinalKamboj true 但提供的链接是针对方法本身的。要使用它,他需要异步 form_load 并使用 await 关键字。
  • 请查看下面的解决方案,我们不需要Form_Load作为Async
猜你喜欢
  • 2014-04-12
  • 1970-01-01
  • 2021-11-13
  • 2018-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-10
  • 1970-01-01
相关资源
最近更新 更多