C#事件驱动的状态机需要使用Windows.Forms.Timer释放事件提升器

时间:2011-07-20 14:14:40

标签: c# timer state-machine event-driven

在我们的旧版VB6代码中,我们使用系统计时器来执行回调,以便可以在不阻塞事件处理程序的情况下运行状态机。一些Psuedo代码示例

Sub Unhooked(State info)
    Select Case info
        Case 1
            NextState = somestate1
        Case 2
            NextState = somestate2
        Case 3
            NextState = somestate3
    End Select
    RunStateMachine
End Sub

Sub RunStateMachine()
    MyObject.GoDoSomethingAndCallMeBack
End Sub

Sub MyObject_EventCallback(State info)
    APITimer.SetUpCallBackTarget (Unhooked, info)
    APITimer.CallMeBackInASec
End Sub

希望您了解在状态机中进行某些调用的情况,该函数将在事件处理程序被触发后生成,并设置一个计时器来执行回调,以便在我们继续之前完成事件处理程序代码到下一个州。

我在C#中继续使用类似的方法,但感觉不对,因为我确信这种语言会提供一种可行的方法。由于我的C#状态机仍在调用在UI线程上引发事件的VB6对象,是否有更好的方法来“解除”事件处理程序,以便在状态机继续运行之前释放它?

我以为我可以使用BeginInvoke向泵添加消息以运行状态机,但是在同一个线程而不是线程池线程但我的类不是表单或控件。我想我可以提出一个不使用计时器的解决方案,但是,它们很容易使用,所以任何想法都会很棒。

1 个答案:

答案 0 :(得分:1)

要解决您的直接问题,您可以将Task排队到UI线程上下文,例如:

var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() => Unhooked(info), CancellationToken.None,
    TaskCreationOptions.None, ui);

即使您的对象不是表单或控件,也可以检测到UI SynchronizationContext。有关SynchronizationContext的详细信息,请参阅my MSDN article

如果你的代码中有很多这个,你可能想把它包装成一个帮助方法:

public void CallbackLater(Action action)
{
  Task.Factory.StartNew(action, CancellationToken.None,
    TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}

void MyObject_EventCallback(State info)
{
  CallbackLater(() => Unhooked(info));
}

在更广泛的方案中,认真考虑Async CTP并设计Task-Based Asynchronous Pattern API而不是在UI线程上引发事件(即基于事件的异步模式)。