使用WinForms进行MVP异步调用的最佳实践

Geo*_*uer 8 .net mvp multithreading winforms

我在WinForms项目中使用了Model-View-Presenter模式,我遇到的一个问题是当表单告诉演示者做某事然后在演示者去做时没有反应时.幸运的是,在我的项目中,我所有的presenter调用都是异步的,问题是如何做到这一点?

每个演示者调用是否应该包含在新的线程创建中?*

new Thread(()=>_presenter.DoSomething()).Start();
Run Code Online (Sandbox Code Playgroud)

这里最好的做法是什么?如果用户按下"中止您正在做的事情"按钮怎么办?我该如何优雅地流产?

.*实际上我可能只是在演示者上使用某种代理来执行此操作而不是将线程创建放在WinForm中

Igo*_*ejc 5

我通常将任何可以(实际上)花费超过一两秒的动作放入一个单独的任务中,例如:

public interface ITask
{
    void ExecuteTask (ITaskExecutionContext context);
    void AfterSuccess(ITaskExecutionContext context);
    void AfterFailure(ITaskExecutionContext context);
    void AfterAbortion(ITaskExecutionContext context);
}
Run Code Online (Sandbox Code Playgroud)

我还有一个运行这些任务的抽象:

public interface ITaskExecutor : IDisposable
{
    void BeginTask(ITask task);
    void TellTaskToStop();
}
Run Code Online (Sandbox Code Playgroud)

其中一个实现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();
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)

我非常依赖依赖注入和IoC来连接在一起.在演示者中,我只是称之为:

GoAndDontReturnUntilYouBringMeALotOfMoneyTask task = new GoAndDontReturnUntilYouBringMeALotOfMoneyTask(parameters);
taskExecutor.BeginTask(task);
Run Code Online (Sandbox Code Playgroud)

然后连接取消/中止按钮,以便它们告诉任务执行者/任务中止.

它实际上比这里介绍的要复杂一点,但这是一般的想法.


Pas*_*nen 2

我只能声称我已经考虑过这个问题(在阅读你的问题之前;)。首先,我会在真正重要的地方进行操纵;例如数据库访问阻塞点。如果有一个地方不应该在“UI”上下文中执行(您可以从http://msdn.microsoft.com/en-us/library/system.threading.synchronizationcontext.current.aspx保存它)保存在UI线程中)然后与非 UI 同步上下文进行比较)然后 Debug.BitchAndMoan() 对此进行比较。任何更长的计算(“应该”都清楚地分开在它们自己的流形中,对吧;)应该断言这一点。

我想您至少应该通过属性配置演示者函数的执行类型,然后由代理遵守。(以防万一您想以串行方式完成某些操作)。

取消任务实际上是演示者的问题,但你必须有一个引用对象来告诉你要停止什么。如果您采用代理方式,那么您可以使用 IAsyncResult 将创建的线程拾取到任务列表中,但是如果允许并行调用多次相同的操作,则确定应该取消哪个线程仍然是一个问题。因此,您必须在启动任务时为其提供一个合适的特定于调用的名称;这意味着 View 端有太多逻辑 -> Presenter 可能应该要求 View 询问用户应该处理哪一项任务。

我的经验是,这通常可以通过使用事件(SCSF 风格)来解决。如果从头开始,我会采用代理方式,因为 SCSF 在很多方面都令人痛苦,以至于我怀疑其设计者的理智。