【问题标题】:UI should not be freeze during background processingUI 不应在后台处理期间冻结
【发布时间】:2015-10-09 05:09:02
【问题描述】:

我正在开发需要提高性能的遗留应用程序(winform 应用程序)。

在这个应用程序中,我们使用 MVP 模式和 Shell 使用反射来查找它需要调用哪个 Presenter 以满足用户请求。所以有一个功能可以完成以下任务......

  1. 找到合适的演示者
  2. 遍历所有方法以找出默认方法引用。
  3. 为方法的参数输入准备一个数组
  4. 在presenter上调用默认方法
  5. 返回演示者参考

这里有一些代码...

 public object FindPresenter(Type pType, string action, Dictionary<string, object> paramDictonary, string callerName = null)
        {
            if (pType == null)
            {
                throw new ArgumentNullException("presenterType");
            }
            var presenterTypeName = pType.Name;

            var presenter = _presenterFactory.Create(pType);
            presenter.CallerName = callerName;

            if (presenter == null)
            {
                throw new SomeException(string.Format("Unable to resolve presenter"));
            }

            // Check each interface for the named method
            MethodInfo method = null;
            foreach (var i in presenter.GetType().GetInterfaces())
            {
                method = i.GetMethod(action, BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance);
                if (method != null) break;
            }

            if (method == null)
            {
                throw new SomeException(string.Format("No action method found"));
            }

            // Match up parameters
            var par = method.GetParameters();
            object[] results = null;
            if (paramDictonary != null)
            {
                if (par.Length != paramDictonary.Count)
                    throw new ArgumentException(
                        "Parameter mis-match");

                results = (from d in paramDictonary
                           join p in par on d.Key equals p.Name
                           orderby p.Position
                           select d.Value).ToArray();

            }

            // Attach release action
            presenter.ReleaseAction = () => _presenterFactory.Release(presenter);

            // Invoke target method
            method.Invoke(presenter, results);

            return presenter;
        }

此方法大约需要 15-20 秒才能完成并冻结 UI。我想用一些异步处理来反射这个方法,所以在这个方法期间 UI 不会冻结。由于我需要返回演示者参考,我想到了使用 wait() 或 join() 方法,但它们会再次锁定 UI。

请注意,我使用的是 .NET 4.0。

【问题讨论】:

  • 我的问题是如何通过将一些不锁定 UI 的任务放在后台来反映这一点。
  • 您可以使用单独的线程来完成所有过程,然后更新 UI。为什么要在 UI 线程上等待。
  • 我可以这样做...但问题出在此方法的末尾,我需要返回演示者参考...如果我为所有这些工作安排新线程,演示者参考将不会可以退货。

标签: c# winforms asynchronous reflection


【解决方案1】:

除非您要搜索数百万个演示者类型,这是非常值得怀疑的,除非您的普通演示者有数百万个参数,这也是非常值得怀疑的,否则我在上面看到的代码中没有任何内容需要 15 秒执行。

因此,整个延迟不在于您向我们展示的代码,而在于它调用的函数之一。

这可能是在看起来非常可疑的_presenterFactory.Create(pType);paramDictionary 的实现中,如果它碰巧是一个自己滚动的字典而不是标准的哈希字典,或者在@ 的调用中987654323@ 本身。

最有可能在最后。

因此,首先,分析您的代码以找出真正的罪魁祸首。

然后,重构您的代码,使冗长的过程发生在单独的工作线程上。这可能需要您将应用程序的相当一部分从 GUI 线程中拉出并进入该工作线程。没有人说 GUI 编程很容易。

如果罪魁祸首是method.Invoke(),那么这看起来你可以相对容易地转移到另一个线程:在返回之前立即启动该线程,并确保每个演示者对象发生的所有事情都是线程安全。

但是,当然,这些演示者正在尝试在 GUI 中实际渲染内容,那么您将不得不去重构所有这些内容,以将其计算成本高昂的逻辑与渲染逻辑分开,因为 WinForms 只能从在它自己的线程中。

【讨论】:

    【解决方案2】:

    最简单的方法是在foreach 循环中使用Application.DoEvents(); 以在长时间运行的进程中保持UI 解锁

    您可以参考MSDN system.windows.forms.application.doevents
    但在使用之前您还必须阅读 Keeping your UI Responsive and the Dangers of Application.DoEvents

    【讨论】:

      【解决方案3】:

      好吧,根据您的评论:“我的问题是如何通过将一些不锁定 UI 的任务置于后台来反映这一点。”

      试试这个:

      class Program
      {
          static void Main(string[] args)
          {
              Task t1 = Task.Run(() => FindPresenter(typeof(Program), "boo"))
                  .ContinueWith(x => UsePresenter(x.Result));
      
              while (true)
              {
                  Thread.Sleep(200);
                  Console.WriteLine("I am the ui thread. Press a key to exit.");
      
      
                  if ( Console.KeyAvailable)
                      break;
              }
      
          }
      
          static object FindPresenter(Type type, string action)
          {
              // Your FindPresenter logic here
      
              Thread.Sleep(1000);
              return (object)"I'm a presenter";
          }
      
          static void UsePresenter(object presenter)
          {
              Console.WriteLine(presenter.ToString());
              Console.WriteLine("Done using presenter.");
          }
      }
      

      对不起,我不是 Winforms 人...。在 WPF 中,我们有 Dispatcher 来解决线程关联问题。你可以试试这个: System.Windows.Threading.Dispatcher and WinForms?

      【讨论】:

        猜你喜欢
        • 2019-07-03
        • 2023-03-14
        • 1970-01-01
        • 1970-01-01
        • 2017-06-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多