.Net:这是管理应用程序状态的笨拙方法还是聪明方法?(停止|运行|暂停|停止|开始|暂停|继续)

.Net:这是管理应用程序状态的笨拙方法还是聪明方法?(停止|运行|暂停|停止|开始|暂停|继续),.net,windows-services,state,.net,Windows Services,State,我正在开发一个具有一定复杂性的Windows服务应用程序,需要有一种在状态之间优雅过渡的方法。由于每个状态可能需要初始化/清理,因此必须有一种机制来协调它们之间的转换,并确保从任何给定状态只能到达有效状态。所讨论的状态是Windows服务的常见状态: 停止|运行|暂停|停止|开始|暂停|继续 我已经开发了一个系统,似乎可以很好地做到这一点,但我很好奇它是否有我还没有发现的缺陷,和/或是否有更多我可以或应该使用的经过尝试和测试的模式/最佳实践 这是在一个Bootstrapper类中实现的,该类公开

我正在开发一个具有一定复杂性的Windows服务应用程序,需要有一种在状态之间优雅过渡的方法。由于每个状态可能需要初始化/清理,因此必须有一种机制来协调它们之间的转换,并确保从任何给定状态只能到达有效状态。所讨论的状态是Windows服务的常见状态:

停止|运行|暂停|停止|开始|暂停|继续

我已经开发了一个系统,似乎可以很好地做到这一点,但我很好奇它是否有我还没有发现的缺陷,和/或是否有更多我可以或应该使用的经过尝试和测试的模式/最佳实践

这是在一个Bootstrapper类中实现的,该类公开了与我应该从Windows服务中获得的命令相对应的方法:Start、Stop、Pause、Continue(这样服务的OnStart命令将只调用该类的Start方法,OnStop将调用Stop等):

在服务代码中(这是VB.Net,但我不得不在这里使用//作为注释,因为“似乎把代码列表中的事情搞砸了”):

在类的
Start()
方法中(为了简单起见,我绕过了线程和异常处理以及其他逻辑):

然后,在我的类中,有一组类似的私有
OnStart()
OnStop()
OnPause()
OnContinue()
方法,它们对每个状态执行实际的初始化/清理:

Private Function OnStart() As Boolean
    SetState(State.StartPending)
        // Do something
    Return SetState(State.Running)
End Function
正如您在上面所看到的,还有对另外两个方法的调用-
RequestState()
SetState()
。这就是逻辑实际发生的地方,它的工作原理如下:

正在向应用程序发送命令(启动、停止、暂停或继续)。接收方法调用
RequestState()
,并将所需的结束状态作为参数传递。如果该状态是可访问的,则返回
True
,否则返回
False

RequestState()
将使用
Select
构造,根据当前状态和请求的状态确定正确的操作

SetState()
实际设置应用程序的状态,执行此操作后,检查是否有“下一个”状态排队(这样,在发出暂停命令之前,StartPending进程将能够完成并以运行状态结束)

如果我以混乱的顺序介绍这些方法,请原谅,但从我的角度来看,这似乎是介绍功能的正确方式。下面是
RequestState()
SetState()
方法(很抱歉,太长了,但我觉得为了完整性需要它):


我想说这是可以的。州不多,也比较容易遵循。我想说,有些地方你可以把代码缩短一点,但除此之外也不算太糟

但是,创建真实状态表可能更容易。首先在网格中映射状态表,如下所示:

Start State        Requested State      State Changed?
-----------        ---------------      --------------
Exception          { any }              False
Stopped            Running              OnStart()
Paused             Stopped              OnStop()
Paused             Running              OnPause()
StartPending       Running              True
StartPending       Paused               True
etc

现在将此转换为代码,认为这基本上是一个<代码>字典<代码>,其中外部密钥是您的启动状态,外部值是您的转换表。

这段代码没有经过测试,但应该可以让您大致了解从何处开始(使用C#,因为我的VB fu已经生锈了):

public bool RequestState(State requestedState)
{
_nextState=requestedState;
if(requestedState==currentState)
返回true;
var statetransforts=新字典
{
{State.Exception,新字典OnStart()}
}
},
{State.已暂停,新词典
{
{State.Stopped,()=>OnStop()},
{State.Running,()=>OnPause()},
}
},
{State.StartPending,新字典
{
{State.Running,()=>true},
{State.Paused,()=>true},
}
},
//剩余状态
};
变量转换=状态转换[当前状态];
函数传递;
if(transition.TryGetValue(requestedState,out transitionAction))
返回transitionAction();
返回false;
}

除了这个答案之外,由于您有一些VB case语句,我个人发现,通过使用冒号将多个VB语句放在同一行(即
case State.Whatever:Return True
),代码更加紧凑。这是相同的代码,只占一半的空间。哦,你也可以将
{State.Whatever,()=>OnStop()}
重写为
{State.Whatever,OnStop}
。这两种方法都很好,使用一种或另一种都是一种风格选择。谢谢,你太棒了。我曾考虑使用一些逻辑矩阵,但由于目前的不确定原因,我没有使用它。但是,如果您注意到在我的代码中,偶尔也会出现这样的情况(具有挂起状态),其中需要在case语句中设置_nextState,同时返回bool。我认为_nextState特性以及RequestState与SetState的联系可能很聪明。我最初(也许最后)使用纯Select语句的原因是,在编写代码的同时,我正在处理自己的逻辑,并且需要保持简单,在基本原则确定之前不要混淆自己:)
Private Function OnStart() As Boolean
    SetState(State.StartPending)
        // Do something
    Return SetState(State.Running)
End Function
Private _currentState As State
Private _nextState As State

Private Function RequestState(ByVal requestedState As State) As Boolean
    Select Case requestedState
        Case State.StartPending, State.StopPending, State.PausePending, State.ContinuePending, State.Exception, State.None
            Throw New ArgumentException(requestedState.ToString & " cannot be requested directly.")
        Case Else

            _nextState = requestedState

            Select Case _currentState
                Case State.Exception
                    Return False

                Case State.Stopped
                    Select Case requestedState
                        Case State.Stopped
                            Return True
                        Case State.Running
                            Return OnStart()
                        Case Else
                            Return False
                    End Select

                Case State.Running
                    Select Case requestedState
                        Case State.Stopped
                            Return OnStop()
                        Case State.Running
                            Return True
                        Case State.Paused
                            Return OnPause()
                        Case Else
                            Return False
                    End Select

                Case State.Paused
                    Select Case requestedState
                        Case State.Stopped
                            Return OnStop()
                        Case State.Running
                            Return OnContinue()
                        Case State.Paused
                            Return True
                        Case Else
                            Return False
                    End Select

                Case State.StartPending
                    Select Case requestedState
                        Case State.Stopped
                            _nextState = State.Stopped
                            Return True
                        Case State.Running
                            Return True
                        Case State.Paused
                            _nextState = State.Paused
                            Return True
                        Case Else
                            Return False
                    End Select

                Case State.StopPending
                    Select Case requestedState
                        Case State.Stopped
                            Return True
                        Case State.Running
                            _nextState = State.Running
                            Return True
                        Case Else
                            Return False
                    End Select

                Case State.ContinuePending
                    Select Case requestedState
                        Case State.Stopped
                            _nextState = State.Stopped
                            Return True
                        Case State.Running
                            Return True
                        Case State.Paused
                            _nextState = State.Paused
                            Return True
                        Case Else
                            Return False
                End Select

                Case State.PausePending
                    Select Case requestedState
                        Case State.Stopped
                            _nextState = State.Stopped
                            Return True
                        Case State.Running
                            _nextState = State.Running
                            Return True
                        Case State.Paused
                            Return True
                        Case Else
                            Return False
                    End Select

                Case Else
                    Return False

            End Select
    End Select
End Function

Private Function SetState(ByVal newState As State) As Boolean
    _currentState = newState
    If newState = State.Running OrElse newState = State.Stopped OrElse newState = State.Paused Then
        If _currentState = _nextState Then
            Return True
        End If
        Return RequestState(_nextState)
    Else
        Return True
    End If
End Function
Start State        Requested State      State Changed?
-----------        ---------------      --------------
Exception          { any }              False
Stopped            Running              OnStart()
Paused             Stopped              OnStop()
Paused             Running              OnPause()
StartPending       Running              True
StartPending       Paused               True
etc
public bool RequestState(State requestedState)
{
    _nextState = requestedState;
    if (requestedState == currentState)
        return true;

    var stateTransitions = new Dictionary<State, Dictionary<State, Func<bool>>>
    {
        { State.Exception, new Dictionary<State, Func<bool>(),
        { State.Stopped, new Dictionary<State, Func<bool>>
            {
                { State.Running, () => OnStart() }
            }
        },
        { State.Paused, new Dictionary<State, Func<bool>>
            {
                { State.Stopped, () => OnStop() },
                { State.Running, () => OnPause() },
            }
        },
        { State.StartPending, new Dictionary<State, Func<bool>>
            {
                { State.Running, () => true },
                { State.Paused, () => true },
            }
        },
        // remaining states
    };

    var transition = stateTransitions[currentState];
    Func<bool> transitionAction;
    if (transition.TryGetValue(requestedState, out transitionAction))
        return transitionAction();
    return false;
}