【问题标题】:Async methods in C#C# 中的异步方法
【发布时间】:2012-05-30 23:13:58
【问题描述】:

我有几个进程需要在 Windows 窗体应用程序的后台运行,因为它们需要太多时间,而且我不想在它们完全完成之前冻结用户界面,我希望有一个指示器来显示每个操作的过程,到目前为止我有一个表单来显示每个操作的进度但是我的操作是同步运行的。

所以我的问题是异步运行这些操作(访问数据库)的最简单方法是什么?

我忘记了应用程序需要的一项重要功能,用户可以随时选择取消任何操作。我认为这个要求使应用程序复杂化了很多,至少就我目前的技能而言,所以基本上我想强调我需要一个易于理解且易于实施的解决方案。我知道会有一些好的做法可以遵循,但在这一点上,我希望一些代码稍后可以工作,我会花更多时间重构代码

【问题讨论】:

    标签: c#


    【解决方案1】:

    .NET 4 添加了Task Parallel Library,它提供了一种非常简洁的机制来使同步操作异步。

    它允许您将同步操作包装到一个 Task 中,然后您可以等待,或使用延续(一些在任务完成时执行的代码)。

    这通常看起来像:

    Task processTask = Task.Factory.StartNew(() => YourProcess(foo, bar));
    

    一旦你有了任务,你就有很多选择,包括阻止:

    // Do other work, then:
    processTask.Wait(); // This blocks until the task is completed
    

    或者,如果您想要继续(代码在完成后运行):

    processTask.ContinueWith( t => ProcessCompletionMethod());
    

    您也可以使用它来组合多个异步操作,并在其中任何一个或全部完成时完成,等等。

    请注意,以这种方式使用 TaskTask<T> 有另一个巨大的优势 - 如果您稍后迁移到 .NET 4.5,您的 API 将使用新的 async/await 语言按原样工作,无需更改代码C# 5 中的功能。

    我忘记了应用程序需要的一项重要功能,用户可以随时选择取消任何操作。

    TPL 从一开始就被设计为与 .NET 4 的新 cooperative cancellation model 一起很好地工作。这使您可以拥有一个 CancellationTokenSource,它可用于取消您的任何或所有任务.

    【讨论】:

      【解决方案2】:

      在 C# 中,有几种方法可以做到这一点

      我个人建议您尝试响应式扩展

      http://msdn.microsoft.com/en-us/data/gg577609.aspx

      你实际上可以这样做:

      https://stackoverflow.com/a/10804404/1268570

      我为你创建了这个,虽然它不是线程安全的,但这确实很容易,但这是一个很好的起点

      在表格中

      var a = Observable.Start(() => Thread.Sleep(8000)).StartAsync(CancellationToken.None);
      var b = Observable.Start(() => Thread.Sleep(15000)).StartAsync(CancellationToken.None);
      var c = Observable.Start(() => Thread.Sleep(3000)).StartAsync(CancellationToken.None);
      
      Manager.Add("a", a.ObserveOn(this).Subscribe(x => MessageBox.Show("a done")));
      Manager.Add("b", b.ObserveOn(this).Subscribe(x => MessageBox.Show("b done")));
      Manager.Add("c", c.ObserveOn(this).Subscribe(x => MessageBox.Show("c done")));
      
      private void button1_Click(object sender, EventArgs e)
      {
          Manager.Cancel("b");
      }
      

      管理器实用程序

      public static class Manager
      {
          private static IDictionary<string, IDisposable> runningOperations;
      
          static Manager()
          {
              runningOperations = new Dictionary<string, IDisposable>();
          }
      
          public static void Add(string key, IDisposable runningOperation)
          {
              if (runningOperations.ContainsKey(key))
              {
                  throw new ArgumentOutOfRangeException("key");
              }
      
              runningOperations.Add(key, runningOperation);
          }
      
          public static void Cancel(string key)
          {
              IDisposable value = null;
              if (runningOperations.TryGetValue(key, out value))
              {
                  value.Dispose();
                  runningOperations.Remove(key);
              }
          }
      

      【讨论】:

        【解决方案3】:

        如果 ORM/数据库 API 本身没有异步方法,请查看 BackgroundWorker Class。它支持取消 (CancelAsync/CancellationPending) 和进度报告 (ReportProgress/ProgressChanged)。

        【讨论】:

        • BackgroundWorker 确实可以工作,但在使用异步操作时确实有一些巨大的缺点。处理多个任务更加困难(即:在所有任务都完成后运行某些东西)。
        • 是的。另一方面,任务的进度报告比 BackgroundWorker 更难。
        • 我不同意 - 如果您从 UI 上下文创建 TaskScheduler,报告会更容易,因为您不必将要报告的信息映射到传递给 ReportProgress 的内容中事件。 “报告”变成了安排另一个任务。
        • 如果您的“报告”很简单,即:进度条 ;)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多