具有句柄长期任务的MVP

时间:2016-10-27 09:34:02

标签: c# .net multithreading asynchronous mvp

我正在编写使用.net C#在Winform上应用MVP(Model,View,Presenter)的软件。我在设计处理长期任务的Presenter类时遇到了麻烦(需要时间来计算和响应)。 我在互联网上研究并知道如何在.net 4.5上使用TPL Task或async / await。但是,这样做,我需要从视图中为每个动作应用它。

现在,我想设计一个从视图接收动作的Presenter类,在其他线程中自动执行它们(比如使用Task,ThreadPool,...)并将结果更新回GUI而不会出现任何跨线程异常。

示例:我想控制Robot,我有RobotView,RobotPresenter和RobotController,它们处理要发送给Rotbot devive的所有动作 在RobotView Class,我处理一个动作HomeRobot,调用演示者。

 Private Sub btnHome_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnHome.Click
        AVPLib.Log.guiLogger.Info("Enter btnHome_Click")
        Dim strMessageText As String = String.Empty
        Try
            strMessageText = AVPLib.ContainerData.GetMessageText("HomeButtonCenterRobotCassettes")
            If Utils.ShowAVPMessageBox(strMessageText, HOME_TM, MessageBoxIcon.Question) = DialogResult.OK Then
                **_presenter.HomeRobot()**
                AVPLib.ContainerData.LogAlarmEvent(AVPLib.ContainerData.TypeUser, AVPLib.ContainerData.LogSource.AVPMainScreen,
                                                             "[Main Screen]" & " Home Click")
            End If
        Catch ex As Exception
            AVPLib.Log.avpLogger.Error(ex.ToString())
        End Try
        AVPLib.Log.guiLogger.Info("Leave btnHome_Click")
    End Sub

在Presenter:我调用RobotController(Model)来执行需要很长时间的Home动作(向设备发送命令,等待返回响应)

  Public Function HomeRobot() As Boolean
        Dim result As Boolean = False
        HEventLoggerWrapper.Debug("Enter HomeRobot.")

        Try
            _robotView.EnableDisableAllButton(False)
            Dim taskResult As Task(Of Boolean) = Task.Run(Function() _robotController.SafetyHomeRobot())
            taskResult.GetAwaiter().OnCompleted(Sub() result = taskResult.Result)

        Catch ex As Exception
            LogParameterUtility.LogErrorParameter(HEventLoggerWrapper.Logger,
                                                   [GetType]().Name,
                                                   "HomeRobot",
                                                   MethodBase.GetCurrentMethod(), ex)
        End Try

        HEventLoggerWrapper.Debug("Leave HomeRobot. Result = " & result)
        Return result
    End Function

所以,它有效但我需要对所有动作应用Task.Run/GetAwaiter ...(ArmUp,ArmDown,Extend,Retract,...)。我也为其他演示者(其他设备)做同样的事情。这是浪费时间。

我想设计一种方法,来自视图调用Presenter.DoSomeThing的每个函数都会自动在另一个线程上运行,因为它们是长期运行的任务。

希望有人可以提供帮助

此致

1 个答案:

答案 0 :(得分:0)

此问题有两种可能的方法。最优雅,可能是最佳实践" (如果我错了,请随意纠正我)是使处理程序方法本身 async void 。这里要说的是:这是唯一的情况,你应该使用async void。否则总是使用async Task

如果您不想让处理程序异步,您也可以使用Task.Factory.StartNew(PresenterActionMethod).ContinueWith(AnotherHandler);,但这是一种相对糟糕的方法。您将需要另一个处理程序方法,该方法将从另一个线程调用,因此如果您更改任何UI控件,则必须在UI线程上调用。使用Task.Wait(...)不会起作用,因为它会阻止UI线程。

这方面的信息很少。但是像这样使用它可以很好地工作。

///=================================================================================================
/// <summary>   Handler in your View class. </summary>
///
/// <param name="sender">   Source of the event. </param>
/// <param name="e">        Event information. </param>
///=================================================================================================
public async void ViewEventHandler(object sender, EventArgs e)
{
    // Set some UI stuff
    // Here we need to use ConfigureAwait(true) since we will interact with the UI after this method call
    await PresenterActionMethod().ConfigureAwait(true);

    // Set some UI stuff again
}

///=================================================================================================
/// <summary>
///     This method is inside your Presenter class and is expected to return once the long
///     running method of your Model is finished.
/// </summary>
///
/// <returns>   A Task. </returns>
///=================================================================================================
public async Task PresenterActionMethod()
{
    // Do stuff
    await ModelLongRunningTaskMethod().ConfigureAwait(false);
    // Do other stuff
}

///=================================================================================================
/// <summary>
///     This method is inside your Model class and is expected to be a long-running method.
/// </summary>
///
/// <returns>   A Task. </returns>
///=================================================================================================
public async Task ModelLongRunningTaskMethod()
{
    // ... do your stuff here
    // You should use ConfigureAwait(false) here since you don't need the captured context
    // this improves performance and reduces the risk of deadlocks
    await Task.Delay(6000).ConfigureAwait(false);
}