在后台处理期间不应冻结UI

时间:2015-10-09 05:09:03

标签: c# winforms asynchronous reflection

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

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

  1. 找到合适的演示者
  2. 通过它来查找默认方法参考的所有方法。
  3. 为方法的参数输入准备一个数组
  4. 在演示者
  5. 上调用默认方法
  6. 返回演示者参考
  7. 这里有一些代码......

     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不会冻结。因为我需要返回presenter引用,我想过使用wait()或join()方法,但是它们会再次锁定UI。

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

3 个答案:

答案 0 :(得分:1)

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

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

如果它恰好是一个自己动手的字典而不是标准的哈希字典,那么可能会出现在_presenterFactory.Create(pType);paramDictionary的实现中。 method.Invoke(presenter, results);本身的调用。

最有可能在最后。

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

然后,重构您的代码,以便在单独的工作线程上发生冗长的进程。这可能需要您将应用程序的大部分内容从GUI线程中提取到该工作线程中。没有人说GUI编程很容易。

如果罪魁祸首是method.Invoke(),那么这看起来就像你可以相对容易地移动到另一个线程:在返回之前启动该线程,并确保每个主持人发生的一切对象是线程安全的。

但是,当然,这些演示者试图在GUI中实际渲染内容,然后你必须去重构所有这些东西,将他们计算上昂贵的逻辑与渲染逻辑分开,因为WinForms只能从在自己的线程中。

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

好吧,基于你的评论:“我的问题是如何通过在后台放置一些不锁定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?