我在WinForms项目中使用了Model-View-Presenter模式,我遇到的一个问题是当表单告诉演示者做某事然后在演示者去的时候没有反应做到这一点。幸运的是,在我的项目中,我对所有presenter调用异步都没有问题,问题是如何做到这一点?
每个演示者调用是否应该包含在新的线程创建中?*
new Thread(()=>_presenter.DoSomething()).Start();
这里最好的做法是什么?如果用户按下“中止您正在做的事情”按钮怎么办?我如何优雅地流产?
。*实际上我可能只是在演示者上使用某种代理来执行此操作而不是将线程创建放在WinForm中
答案 0 :(得分:5)
我通常将任何可以(实际上)花费超过一两秒的动作放入一个单独的任务中,例如:
public interface ITask
{
void ExecuteTask (ITaskExecutionContext context);
void AfterSuccess(ITaskExecutionContext context);
void AfterFailure(ITaskExecutionContext context);
void AfterAbortion(ITaskExecutionContext context);
}
我也有运行这些任务的抽象:
public interface ITaskExecutor : IDisposable
{
void BeginTask(ITask task);
void TellTaskToStop();
}
此ITaskExecutor
的其中一个实现是使用BackgroundWorker
:
public class BackgroundTaskExecutor : ITaskExecutor
{
public void BeginTask(ITask task)
{
this.task = task;
worker = new BackgroundWorker ();
worker.DoWork += WorkerDoWork;
worker.RunWorkerCompleted += WorkerRunWorkerCompleted;
worker.WorkerSupportsCancellation = true;
worker.RunWorkerAsync();
}
...
}
我非常依赖依赖注入和IoC将事物连接在一起。在演示者中,我只是称之为:
GoAndDontReturnUntilYouBringMeALotOfMoneyTask task = new GoAndDontReturnUntilYouBringMeALotOfMoneyTask(parameters);
taskExecutor.BeginTask(task);
然后连接取消/中止按钮,以便它们告诉任务执行者/任务中止。
它实际上比这里介绍的要复杂一点,但这是一般的想法。
答案 1 :(得分:2)
我只能声称我已经考虑过这个问题(先阅读你的问题;)。首先,我会在那些真正重要的地方进行钻探;例如DB访问阻塞点。如果有一个不应该在“UI”上下文中执行的地方(您可以在UI线程中将其从http://msdn.microsoft.com/en-us/library/system.threading.synchronizationcontext.current.aspx保存,然后再将其与非UI同步上下文进行比较),然后再调试它的Debug.BitchAndMoan()。任何更长的计算(“应该”都应该在各自的流形中明确分开,正确;)应该断言。
我想你至少应该通过属性配置presenter函数的执行类型,然后由代理服从。 (以防你想要以连续方式完成某些事情)。
取消任务实际上是演示者的问题,但您必须有一个引用对象来告诉您要停止的内容。如果你采用代理方式,那么你可以使用IAsyncResult将已创建的线程提取到任务列表中,但如果允许多个并行调用相同的操作,则确定应该取消哪个线程仍然是一个问题。因此,您必须在启动时为任务提供适当的特定于呼叫的名称;这意味着View方面的逻辑过多 - >演示者可能会要求View询问用户应该处理哪个任务。
我的经验是,这通常只是通过使用事件(SCSF样式)来解决。如果从头开始,我会采用代理方式,因为SCSF在很多方面都很痛苦,我怀疑它的设计师的理智。
答案 2 :(得分:0)
为什么不让你使用的代理模式接受几个回调来返回结果或中止?