In our legacy VB6 code we used a system timer to perform a callback so that state machines could be run without blocking event handlers. Some Psuedo code example
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
Hopefully you get the idea where some call is made in the state machine that will result later on in the event handler being fired and it sets up a timer to do a callback so that the event handler code can complete before we move on to the next state.
I am continuing a similar vein in C# but it feels wrong as I'm sure the language would provide a cuter way of doing this. Since my C# state machine is still calling VB6 objects that raise events on the UI thread, is there a better way of 'unhooking' the event handler so that it is released before the state machine continues on it's way?
I thought I could use BeginInvoke to add a message to the pump to run the state machine but on the same thread instead of a thread pool thread but my class isn't a form or control. I think I could come up with a solution that doesn't use timers but, well they are dead simple to use for this so any ideas would be great.
To solve your immediate problem, you could queue a Task
to the UI thread context, e.g.:
var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() => Unhooked(info), CancellationToken.None,
TaskCreationOptions.None, ui);
The UI SynchronizationContext
can be detected even though your object is not a form or control. For more information on SynchronizationContext
, see my MSDN article.
If you have this in your code a lot, you'll probably want to wrap it up into a helper method:
public void CallbackLater(Action action)
{
Task.Factory.StartNew(action, CancellationToken.None,
TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}
void MyObject_EventCallback(State info)
{
CallbackLater(() => Unhooked(info));
}
In the broader scheme of things, seriously consider the Async CTP and designing a Task-Based Asynchronous Pattern API instead of having the events raised on the UI thread (i.e., the Event-Based Asynchronous Pattern).