我正在开发遗留应用程序(winform App),我需要提高性能。
在这个应用程序中,我们使用MVP模式和Shell使用反射来查找它需要调用哪个演示者以满足用户请求。所以有一个功能可以执行以下任务......
这里有一些代码......
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。
答案 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?