我正在研究传统应用程序(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在这个方法期间不会冻结。由于我需要返回演示者引用,我想到使用wait()或join()方法,但它们会再次锁定UI。
请注意,我使用的是。net 4.0
除非你有数百万个演示者类型要搜索,这是非常值得怀疑的,除非你的演示者平均有数百万个参数,这也是非常值得怀疑的,否则我上面看到的代码中没有任何东西应该花费15秒来执行。
所以,整个延迟并不在你给我们看的代码中,而是在它调用的一个函数中。
这可能是在非常可疑的_presenterFactory.Create(pType);
或paramDictionary
的实现中,如果它碰巧是一个滚动自己的字典而不是标准哈希字典,或者在method.Invoke(presenter, results);
本身的调用中。
最有可能在最后。
所以,首先,剖析你的代码,找出真正的罪魁祸首是什么。
然后,重构你的代码,使冗长的进程发生在一个单独的工作线程上。这可能需要您将应用程序的相当一部分从GUI线程中取出并放入该工作线程中。没有人说GUI编程很容易。
如果罪魁祸首是method.Invoke()
,那么这看起来像是您可以相对容易地移动到另一个线程:在返回之前启动该线程,并确保每个呈现者对象所发生的一切都是线程安全的。
但是,当然,这些演示者正在尝试在GUI中实际呈现内容,那么你将不得不去重构所有这些内容,将它们的计算昂贵的逻辑从呈现逻辑中分离出来,因为WinForms只能从自己的线程中调用。
最简单的方法是在foreach
循环中使用Application.DoEvents();
,以在长时间运行的进程中保持UI解锁
您可以参考MSDN system.windows.forms.application.doevents
但是在使用它之前,你还必须阅读保持你的UI响应和应用程序的危险。DoEvents
嗯,根据你的评论:"我的问题是我如何通过在后台放置一些不锁定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和WinForms?