I have a multi-thread C# application that uses some recursive functions in a dll. The problem that I have is how to cleanly stop the recursive functions.
The recursive functions are used to traverse our SCADA system’s hierarchical ‘SCADA Object’ data. Traversing the data takes a long time (10s of minutes) depending on the size of our system and what we need to do with the data.
When I start the work I create a background thread so the GUI stays responsive. Then the background worker handles the calling of the recursive function in the dll.
I can send a cancel request to the background worker using CancelAsync but the background worker can’t check the CancellationPending flag because it is blocked waiting of the dll’s recursive function to finish.
Typically there is only 1 recursive function active at a time but there are dozens of recursive functions that are used at various times by different background workers.
As a quick (and really shameful) hack I added a global ‘CodeEnabled’ flag to the dll. So when the GUI does the CancelAsync it also sets the ‘CodeEnabled’ flag to false. (I know I need some of those bad code offsets). Then the dll’s recursive loop checks the ‘CodeEnabled’ flag and returns to the background worker which is finally able to stop.
I don’t want to move the recursive logic to the background worker thread because I need it in other places (e.g. other background workers).
What other approach should be used for this type of problem?
It depends on the design, really. Much recursion can be replaced with (for example) a local stack (
Stack<>) or queue (Queue<>), in which case a cancel flag can be held locally without too much pain. Another option is to use some kind of progress event that allows subscribers to set a cancel flag. A third option is to pass some kind of context class into the function(s), with a (volatile or synchronized) flag that can be set.In any of these cases you should have relatively easy access to a cancel flag to exit the recursion.
with (in your function that accepts the context):
Another interesting option is to use iterator blocks for your long function rather than regular code; then your calling code can simply stop iterating when it has had enough.