two-state water tank modeling - c#

In an online test question, I was asked to complete the code for a multithread water tank monitor. When one tries to Empty() an empty WaterTank, one has to wait until it's filled, and the program has to block other threads from calling the same method when it isEmpty(). Similarly, to Fill() a filled WaterTank, one has to wait until it isEmpty(), and the program has to block other threads from the same method again until a state changex`.
I was given the code defining the WaterTank class, which consisted only of a private boolean empty having a public getter and setter,
public class WaterTank
{
private bool empty = true;
public virtual bool IsEmpty()
{
return empty;
}
public virtual void SetEmpty (bool b)
{
// Something is wrong if b == empty
empty = b;
Console.WriteLine(empty ? "Empty":"Full");
}
}
and the code of the WaterTankMonitor class to be completed. The given code for each method contained an empty while loop and a call to SetEmpty().
using System;
public class WaterTankMonitor
{
/// The water tank that this class monitors.
private WaterTank tank;
public WaterTankMonitor(WaterTank tank)
{
this.tank = tank;
}
/// Empty the water tank.
public void Empty()
{
while (tank.IsEmpty())
{
}
tank.SetEmpty (true);
}
/// Fill the water tank.
public void Fill()
{
while (!tank.IsEmpty())
{
}
tank.SetEmpty(false);
}
}
I tried using lock in a similar way as the linked code, but I added readonly as that's recommended. I don't know why the system said my response was wrong.
public class WaterTankMonitor
{
private WaterTank tank;
private readonly object o = new object();
public WaterTankMonitor(WaterTank tank)
{
this.tank = tank;
}
public void Empty()
{
lock(o)
{
while (tank.IsEmpty())
{
}
tank.SetEmpty(true);
}
}
public void Fill()
{
lock(o)
{
while (!tank.IsEmpty())
{
}
tank.SetEmpty(false);
}
}
}
Related Java code.
(Edited)
I am supposed to program the WaterTankMonitor class, and I don't have the right to change the WaterTank class and the following test code.
WaterTank tank = new WaterTank();
WaterTankMonitor monitor = new WaterTankMonitor(tank);
monitor.Fill();
monitor.Empty();

Think: What happens with the lock+while loop? When is the lock released? If one thread tries emptying the tank and finds it empty, can another thread fill it in the meantime?
This should lead you to the root of the problem.
As a side note, this:
while (tank.IsEmpty())
is just brutal.
These things are better done with i.e. condition variables.. in "put the thread to sleep until X changes" style. But of course, not every platform provides condition variables, and using them is a bit more complicated than busy-looping.

Related

SetActive() can only be called from the main thread

I am stuck with this problem for 3 days, I did a lot of research, but couldn't find any answer, Here is a brief explanation of what is happening, trying to work with Firebase Database and Authentication with Unity3D, Here are the steps:
First user signs in and if it's successful, it will fetch user's data from Database and then Authentication panel should be deactivated and User's panel activated.
It Gives me this error when trying to SetActive panel.
SetActive can only be called from the main thread.
Constructors and field initializers will be executed from the loading thread when loading a scene.
Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.
UnityEngine.GameObject:SetActive(Boolean)
public void SignInWithEmail()
{
auth.SignInWithEmailAndPasswordAsync(email, password).ContinueWith(task => {
DatabaseReference.GetValueAsync().ContinueWith(task => {
//here after successful signing in, it gets the data from the Database
//and after that it should activate the user panel
//and deactivate the authentication panel
//HERE IS THE PROBLEM
userPanel.SetActive(true);
authPanel.SetActive(false);
}
}
}
I'm not trying to load another scene or anything else.
If needed I can provide more information
So my answer is very similar to the accepted answer from Milod's, but a little different, as it took me a while to wrap my head around his, even though his still works.
The Issue:
Normally, all your code runs on a single thread in Unity, since Unity is single-threaded,
however when working with APIs like Firebase, which require callbacks, the callback functions will be handled by a new thread.
This can lead to race-conditions, especially on a single-threaded engine like Unity.
The solution (from Unity):
Starting from Unity 2017.X, unity now requires changes to UI components to be run on the Main thread (i.e. the first thread that was started with Unity).
What is impacted ?:
Mainly calls that modify the UI like...
gameObject.SetActive(true); // (or false)
textObject.Text = "some string" // (from UnityEngine.UI)
How this relates to your code:
public void SignInWithEmail() {
// auth.SignInWithEmailAndPasswordAsyn() is run on the local thread,
// ...so no issues here
auth.SignInWithEmailAndPasswordAsync(email, password).ContinueWith(task => {
// .ContinueWith() is an asynchronous call
// ...to the lambda function defined within the task=> { }
// and most importantly, it will be run on a different thread, hence the issue
DatabaseReference.GetValueAsync().ContinueWith(task => {
//HERE IS THE PROBLEM
userPanel.SetActive(true);
authPanel.SetActive(false);
}
}
}
Suggested Solution:
For those calls which require callback functions, like...
DatabaseReference.GetValueAsync()
...you can...
send them to a function which is set up to run on that initial thread.
...and which uses a queue to ensure that they will be run in the order that they were added.
...and using the singleton pattern, in the way advised by the Unity team.
Actual solution
Place the code below into your scene on a gameObject that will always be enabled, so that you have a worker that...
always runs on the local thread
can be sent those callback functions to be run on the local thread.
using System;
using System.Collections.Generic;
using UnityEngine;
internal class UnityMainThread : MonoBehaviour
{
internal static UnityMainThread wkr;
Queue<Action> jobs = new Queue<Action>();
void Awake() {
wkr = this;
}
void Update() {
while (jobs.Count > 0)
jobs.Dequeue().Invoke();
}
internal void AddJob(Action newJob) {
jobs.Enqueue(newJob);
}
}
Now from your code, you can simply call...
UnityMainThread.wkr.AddJob();
...so that your code remains easy to read (and manage), as shown below...
public void SignInWithEmail() {
auth.SignInWithEmailAndPasswordAsync(email, password).ContinueWith(task => {
DatabaseReference.GetValueAsync().ContinueWith(task => {
UnityMainThread.wkr.AddJob(() => {
// Will run on main thread, hence issue is solved
userPanel.SetActive(true);
authPanel.SetActive(false);
})
}
}
}
So basically UI elements need to be modified in Main thread, and I found this script and it will execute your function in Main thread, just put your function in a Coroutine and Enqueue it to the script(UnityMainThreadDispatcher). (You need an object in the scene and add the MainThreadDispathcer script to it)
Here's how my Function looked:
public void SignInWithEmail()
{
auth.SignInWithEmailAndPasswordAsync(email, password).ContinueWith(task => {
DatabaseReference.GetValueAsync().ContinueWith(task => {
//Here's the fix
UnityMainThreadDispatcher.Instance().Enqueue(ShowUserPanel());
}
}
}
public IEnumerator ShowUserPanel()
{
uiController.userPanel.panel.SetActive(true);
uiController.authPanel.SetActive(false);
yield return null;
}
This is the script to run it in Main Thead
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
public class UnityMainThreadDispatcher : MonoBehaviour {
private static readonly Queue<Action> _executionQueue = new Queue<Action>();
/// <summary>
/// Locks the queue and adds the IEnumerator to the queue
/// </summary>
/// <param name="action">IEnumerator function that will be executed from the main thread.</param>
public void Enqueue(IEnumerator action) {
lock (_executionQueue) {
_executionQueue.Enqueue (() => {
StartCoroutine (action);
});
}
}
/// <summary>
/// Locks the queue and adds the Action to the queue
/// </summary>
/// <param name="action">function that will be executed from the main thread.</param>
public void Enqueue(Action action)
{
Enqueue(ActionWrapper(action));
}
IEnumerator ActionWrapper(Action a)
{
a();
yield return null;
}
private static UnityMainThreadDispatcher _instance = null;
public static bool Exists() {
return _instance != null;
}
public static UnityMainThreadDispatcher Instance() {
if (!Exists ()) {
throw new Exception ("UnityMainThreadDispatcher could not find the UnityMainThreadDispatcher object. Please ensure you have added the MainThreadExecutor Prefab to your scene.");
}
return _instance;
}
void Awake() {
if (_instance == null) {
_instance = this;
DontDestroyOnLoad(this.gameObject);
}
}
public void Update() {
lock(_executionQueue) {
while (_executionQueue.Count > 0) {
_executionQueue.Dequeue().Invoke();
}
}
}
void OnDestroy() {
_instance = null;
}
}
Note that Firebase has now a nice ContinueWithOnMainThread extension method that solves this problem more elegantly than the other suggested answers:
using Firebase.Extensions;
public void SignInWithEmail() {
// This code runs on the caller's thread.
auth.SignInWithEmailAndPasswordAsync(email, password).ContinueWith(task => {
// This code runs on an arbitrary thread.
DatabaseReference.GetValueAsync().ContinueWithOnMainThread(task => {
// This code runs on the Main thread. No problem.
userPanel.SetActive(true);
authPanel.SetActive(false);
}
}
}```

Main thread coroutine using Queue<Action>

I am having a little trouble when i get some data from websockets and try to display it through coroutines.
First, I have a classA attached to an object that opens the websocket and displays the data I receive:
public class ClassA : MonoBehaviour {
...
public IEnumerator ConnectWebSocket(url)
{
// in the websocket class, start the websocket connection, which
// will return data through a callback inside the return string
WebSocketClass.WebSocketStart(url, delegate(string result)
{
// receive websocket data and call the functions that displays it
WebSocketData(result);
});
// wait for the socket connection
while (WebSocketClass.WebSocketGetState() == WebSocketSharp.WebSocketState.CONNECTING)
{
yield return 0;
}
if (WebSocketClass.WebSocketGetState() == WebSocketSharp.WebSocketState.OPEN)
{
break;
}
...
}
// function that gets websocket data and starts couroutine to display it
public void WebSocketData(string data)
{
StartCoroutine(DisplayMessage(data));
}
}
But Unity complains with the next error:
StartCoroutine_Auto can only be called
from the main thread. Constructors and
field initializers will be executed
from the loading thread when loading a
scene. Don't use this function in the
constructor or field initializers,
instead move initialization code to
the Awake or Start function.
I searched in the unity forum and found this solution:
public class ClassA : MonoBehaviour {
...
public IEnumerator ConnectWebSocket(url)
{
// in the websocket class, start the websocket connection, which
// will return data through a callback inside the return string
WebSocketClass.WebSocketStart(url, delegate(string result)
{
// receive websocket data and call the functions that displays it
WebSocketData(result);
});
// wait for the socket connection
while (WebSocketClass.WebSocketGetState() == WebSocketSharp.WebSocketState.CONNECTING)
{
yield return 0;
}
if (WebSocketClass.WebSocketGetState() == WebSocketSharp.WebSocketState.OPEN)
{
break;
}
...
}
// function that gets websocket data and starts couroutine to display it
public void WebSocketData(string data)
{
DoOnMainThread.ExecuteOnMainThread.Enqueue(() => { StartCoroutine(DisplayMessage(data)); });
}
}
// class to manage the websocket data display inside the main thread
public class DoOnMainThread : MonoBehaviour
{
public readonly static Queue<Action> ExecuteOnMainThread = new Queue<Action>();
public virtual void Update()
{
// dispatch stuff on main thread
while (ExecuteOnMainThread.Count > 0)
{
ExecuteOnMainThread.Dequeue().Invoke();
}
}
}
And it works! the problem is that even though I wrote the two classes in the same cs file and attached to an object, when I change the scene, return to that scene, and receive any data from the websocket, the next error is displayed:
MissingReferenceException: The object
of type 'ClassA' has been destroyed
but you are still trying to access it.
Your script should either check if it
is null or you should not destroy the
object.
UnityEngine.MonoBehaviour.StartCoroutine
(IEnumerator routine) (at
C:/BuildAgent/work/d63dfc6385190b60/artifacts/EditorGenerated/UnityEngineMonoBehaviour.cs:62)
I tried not destroying the object when a new scene is loaded, as the documentation says:
void Awake()
{
DontDestroyOnLoad(transform.gameObject);
}
But the error still appears.
The weird thing is that although there is an error, the data received from the websocket is displayed without any problem.
Does someone know how to avoid this problem? Any way to trigger a coroutine inside the main thread without using a second class? Or other solution to avoid this error?
Thanks!
I found the problem:
public readonly static Queue<Action> ExecuteOnMainThread = new Queue<Action>();
It is static, so it becomes a problem when a public class is instantiated and generates another ExecuteOnMainThread.
So just deleted "static" and made it destroy and generate itself every time ClassA is created by Unity.
Now it works like a charm : )

Subscribing to EF changes with RX

I'm probably totally misunderstanding what RX is all about, but I thought it would be a neat way of allowing various client applications in my code to subscribe to notifications of changes to certain Entity Framework Code First types.
So in my UOW Commit methood I have
var changes = DbContext.ChangeTracker.Entries<EntEvent>().Where(ee => ee.State != EntityState.Unchanged);
Hub.Instance.NotifyBeforeSave(changes);
and my (rather basic) hub class looks like this...
public sealed class Hub
{
private static readonly Hub instance = new Hub();
static Hub(){}
private Hub(){}
public static Hub Instance
{
get { return instance; }
}
public IObservable<System.Data.Entity.Infrastructure.DbEntityEntry<EntEvent>> BeforeSave = new Subject<DbEntityEntry<EntEvent>>();
public void NotifyBeforeSave<T>(IEnumerable<System.Data.Entity.Infrastructure.DbEntityEntry<T>> changes) where T:class
{
var x = changes.Where(c => typeof(T) == typeof(EntEvent)) as IEnumerable<System.Data.Entity.Infrastructure.DbEntityEntry<EntEvent>>;
BeforeSave = x.ToObservable();
}
}
and then I thought I could subscribe a client (observer) by creating an instance of the following and calling attach.
public class SampleConsumer : IObserver<DbEntityEntry<EntEvent>>
{
public void attach()
{
Hub.Instance.BeforeSave.Subscribe(this);
}
public void OnNext(DbEntityEntry<EntEvent> value)
{
var x = value;
}
public void OnError(Exception error)
{
var y = error;
}
public void OnCompleted()
{
}
}
but breakpoints in OnNext and OnError never get called.
I'm probably 180deg away from where I should be, but we have to start somewhere!
The problem is that you don't have an asynchronous source.
DbContext.ChangeTracker.Entries<EntEvent>()
is a collection. You can convert it to an observable using
IEnumerble.ToObservable();
but that does not make it asynchronous. In fact, it will enumerate the collection right away upon subscription. If the collection happens to be empty, it will do nothing at all. Google the difference between cold/hot observables to understand.
You need an asynchronous source, something like an event.
I don't know EF very well, my guess is that the
((IObjectContextAdapter)DbContext).ObjectContext.SavingChanges
event might be what you need.
Good luck!
Plug in Nick's
https://github.com/NickStrupat/EntityFramework.Triggers
https://github.com/NickStrupat/EntityFramework.Rx
He has patterns with and without deriving from his context, that permit:
DbObservable<Context>.FromInserted<Person>();

Is it possible for instance to destroy/delete self?

NOTE: I'm interested in C#,Java and C++ most, but as this is the more academic question any language will do.
I know that this problem is solvable from outside, by using appropriate methods of given languages (calling free, Dispose, or by removing all references to instance).
My idea is that I create an instance, and in the constructor , I start the private timer. When the timer ends it will call some instance method and destroy the variable.
I think that in C# it should be possible to call Dispose on self, when the IDisposable is implemented, but this would not destroy the instace.
In C++ I could call the destructor, but that would lead to the memory leak, plus it is really bad practice.
In Java I have no clue, assigning to this it's not possible as it is final field.
So is there any way for instance, to destroy self?
Your question is very interesting, and I don't know of any other way to do so in C# but to force from the inside of the instance its destruction from the outside. So this is what I came up with to check if it is possible.
You can create the class Foo, which has event that is fired when the specific interval of the timer elapses. The class that is registered to that event (Bar) within event de-registers the event and sets the reference of the instance to null. This is how I would do it, tested and it works.
public class Foo
{
public delegate void SelfDestroyer(object sender, EventArgs ea);
public event SelfDestroyer DestroyMe;
Timer t;
public Foo()
{
t = new Timer();
t.Interval = 2000;
t.Tick += t_Tick;
t.Start();
}
void t_Tick(object sender, EventArgs e)
{
OnDestroyMe();
}
public void OnDestroyMe()
{
SelfDestroyer temp = DestroyMe;
if (temp != null)
{
temp(this, new EventArgs());
}
}
}
public class Bar
{
Foo foo;
public Bar()
{
foo = new Foo();
foo.DestroyMe += foo_DestroyMe;
}
void foo_DestroyMe(object sender, EventArgs ea)
{
foo.DestroyMe -= foo_DestroyMe;
foo = null;
}
}
And in order to test this, you can set up a button click within a Form, something like this, and check it in the debugger:
Bar bar = null;
private void button2_Click(object sender, EventArgs e)
{
if(bar==null)
bar = new Bar();
}
So next time when you click the button, you will be able to see that Bar instance still exists but the Foo instance within it is null although it has been created within the Bar's constructor.
C++: If an object was allocated dynamically, it can delete its this pointer in its own function, provided the this pointer is never used again after that point.
No, there is no way to achieve what you are trying to do in C#.
If you consider an example :
public class Kamikadze {
......
private void TimerTick(..)
{
....
if(itsTime) {
DestroyMe();
}
}
.....
}
var kamikadze = new Kamikadze ();
after a while DestroyMe() will be called that cleans internal data.
But the reference kamikadze (pointer if you wish) is still valid and points to that memory location, so GC will not do anything, will not collect it, and instance of Kamikadze will remain in memory.
For C++ take a look at this:
http://www.parashift.com/c++-faq/delete-this.html
.
The closest thing in C# that I can think of:
On creation, every object stores a reference to itself in the GC root, e.g. by putting the reference into a class static list. Outside of the class, nobody is allowed to store (strong) references to the object. Everybody uses a WeakReference and checks if the Target is still IsAlive before touching the object. That way, the only thing that is keeping the object alive is the static reference.
When the object decides to kill itself, it simply removes the reference from the list. Sooner or later, the GC collects the object. Or, if you are really impatient, call GC.Collect() (ouch!).
But I really really would not recommend this solution!
It's much better put some flag into the class/object to signal whether it's still alive and make everybody check this flag before using the object. This can be combined with the IDisposable solution.
In C++, instances committing suicide are an integral part of the Finite State Machine Pattern:
//Context class contains a pointer to a State object.
void BattleshipGame::SetGameState(IState* state) {
game_state = state;
}
void BattleshipGame::Loss() {
game_state->Loss(this);
}
void BattleshipGame::Idle() {
game_state->Idle(this);
}
void BattleshipGame::FlyBy() {
game_state->FlyBy(this);
}
void BattleshipGame::Attack() {
game_state->Attack(this);
}
void BattleshipGame::Win() {
game_state->Win(this);
}
void BattleshipGame::Load() {
game_state->Loading(this);
}
//State base class contains methods for switching to every state.
class IState {
public:
virtual void Loading(BattleshipGame* context);
virtual void Idle(BattleshipGame* context);
virtual void FlyBy(BattleshipGame* context);
virtual void Attack(BattleshipGame* context);
virtual void Win(BattleshipGame* context);
virtual void Loss(BattleshipGame* context);
protected:
private:
};
//Implementations in the State base class are defined, but empty.
//Derived States only call what they need:
void StateIdle::Loss(BattleshipGame* context) {
//context->SetGameState(new StateLoss());
context->SetGameState(new StateLoss(context));
delete this;
}
void StateIdle::Idle(BattleshipGame* context) {
context->SetGameState(new StateIdle());
delete this;
}
void StateIdle::FlyBy(BattleshipGame* context) {
context->SetGameState(new StateFlyBy());
delete this;
}
void StateIdle::Win(BattleshipGame* context) {
context->SetGameState(new StateWin());
delete this;
}
//Similar design for all other states...
In C#, you're right you can implement IDisposable but the trick is instead of calling Dispose method make use of the using statement.
class Program
{
static void Main(string[] args)
{
using (MyClass obj = new MyClass())
{
obj.SayHello();
}
// obj.SayHello(); // Error: The name 'obj' does not exist in the current context
}
}
class MyClass : IDisposable
{
public void SayHello()
{
Console.WriteLine("Hello");
}
public void Dispose()
{
// Do something (e.g: close some open connection, etc)
}
}
For Reference: microsoft-docs/using-statement
I recommend using NFTLKEY. You can easily get it from the Nuget package. Best of all, it's open source: github project
Easier to understand than the examples here

How to cancel a deeply nested process

I have a class that is a "manager" sort of class. One of it's functions is to signal that the long running process of the class should shut down. It does this by setting a boolean called "IsStopping" in class.
public class Foo
{
bool isStoping
void DoWork() {
while (!isStopping)
{
// do work...
}
}
}
Now, DoWork() was a gigantic function, and I decided to refactor it out and as part of the process broke some of it into other classes. The problem is, Some of these classes also have long running functions that need to check if isStopping is true.
public class Foo
{
bool isStoping
void DoWork() {
while (!isStopping)
{
MoreWork mw = new MoreWork()
mw.DoMoreWork() // possibly long running
// do work...
}
}
}
What are my options here?
I have considered passing isStopping by reference, which I don't really like because it requires there to be an outside object. I would prefer to make the additional classes as stand alone and dependancy free as possible.
I have also considered making isStopping a property, and then then having it call an event that the inner classes could be subscribed to, but this seems overly complex.
Another option was to create a "Process Cancelation Token" class, similar to what .net 4 Tasks use, then that token be passed to those classes.
How have you handled this situation?
EDIT:
Also consider that MoreWork might have a EvenMoreWork object that it instantiates and calls a potentially long running method on... and so on. I guess what i'm looking for is a way to be able to signal an arbitrary number of objects down a call tree to tell them to stop what they're doing and clean up and return.
EDIT2:
Thanks for the responses so far. Seems like there's no real consensus on methods to use, and everyone has a different opinion. Seems like this should be a design pattern...
You can go two ways here:
1) The solution you've already outlined: pass a signaling mechanism to your subordinate objects: a bool (by ref), the parent object itself cloaked in an interface (Foo: IController in the example below), or something else. The child objects check the signal as needed.
// Either in the MoreWork constructor
public MoreWork(IController controller) {
this.controller = controller;
}
// Or in DoMoreWork, depending on your preferences
public void DoMoreWork(IController controller) {
do {
// More work here
} while (!controller.IsStopping);
}
2) Turn it around and use the observer pattern - which will let you decouple your subordinate objects from the parent. If I were doing it by hand (instead of using events), I'd modify my subordinate classes to implement an IStoppable interface, and make my manager class tell them when to stop:
public interface IStoppable {
void Stop();
}
public class MoreWork: IStoppable {
bool isStopping = false;
public void Stop() { isStopping = true; }
public void DoMoreWork() {
do {
// More work here
} while (!isStopping);
}
}
Foo maintains a list of its stoppables and in its own stop method, stops them all:
public void Stop() {
this.isStopping = true;
foreach(IStoppable stoppable in stoppables) {
stoppable.Stop();
}
}
I think firing an event that your subclasses subscribe to makes sense.
You could create a Cancel() method on your manager class, and on each of your other worker classes. Base it on an interface.
The manager class, or classes that instantiate other worker classes, would have to propagate the Cancel() call to the objects they are composed of.
The deepest nested classes would then just set an internal _isStopping bool to false and your long-running tasks would check for that.
Alternatively, you could maybe create a context of some sort that all the classes know about and where they can check for a canceled flag.
Another option was to create a
"Process Cancelation Token" class,
similar to what .net 4 Tasks use, then
that token be passed to those classes.
I am not familiar with this, but if it is basically an object with a bool property flag, and that you pass into each class, then this seems like the cleanest way to me. Then you could make an abstract base class that has a constructor that takes this in and sets it to a private member variable. Then your process loops can just check that for cancellation.
Obviously you will have to keep a reference to this object you have passed into your workers so that it's bool flag can be set on it from your UI.
Your nested types could accept a delegate (or expose an event) to check for a cancel condition. Your manager then supplies a delegate to the nested types that checks its own "shouldStop" boolean. This way, the only dependency is of the ManagerType on the NestedType, which you already had anyway.
class NestedType
{
// note: the argument of Predicate<T> is not used,
// you could create a new delegate type that accepts no arguments
// and returns T
public Predicate<bool> ShouldStop = delegate() { return false; };
public void DoWork()
{
while (!this.ShouldStop(false))
{
// do work here
}
}
}
class ManagerType
{
private bool shouldStop = false;
private bool checkShouldStop(bool ignored)
{
return shouldStop;
}
public void ManageStuff()
{
NestedType nestedType = new NestedType();
nestedType.ShouldStop = checkShouldStop;
nestedType.DoWork();
}
}
You could abstract this behavior into an interface if you really wanted to.
interface IStoppable
{
Predicate<bool> ShouldStop;
}
Also, rather than just check a boolean, you could have the "stop" mechanism be throwing an exception. In the manager's checkShouldStop method, it could simply throw an OperationCanceledException:
class NestedType
{
public MethodInvoker Stop = delegate() { };
public void DoWork()
{
while (true)
{
Stop();
// do work here
}
}
}
class ManagerType
{
private bool shouldStop = false;
private void checkShouldStop()
{
if (this.shouldStop) { throw new OperationCanceledException(); }
}
public void ManageStuff()
{
NestedType nestedType = new NestedType();
nestedType.Stop = checkShouldStop;
nestedType.DoWork();
}
}
I've used this technique before and find it very effective.
Litter your code with statements like this wherever it is most sensible to check the stop flag:
if(isStopping) { throw new OperationCanceledException(); }
Catch OperationCanceledException right at the top level.
There is no real performance penalty for this because (a) it won't happen very often, and (b) when it does happen, it only happens once.
This method also works well in conjunction with a WinForms BackgroundWorker component. The worker will automatically catch a thrown exception in the worker thread and marshal it back to the UI thread. You just have to check the type of the e.Error property, e.g.:
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
if(e.Error == null) {
// Finished
} else if(e.Error is OperationCanceledException) {
// Cancelled
} else {
// Genuine error - maybe display some UI?
}
}
You can flatten your call stack by turning each DoWork() call into a command using the Command pattern. At the top level, you maintain a queue of commands to perform (or a stack, depending on how your commands interact with each other). "Calling" a function is translated to enqueuing a new command onto the queue. Then, between processing each command, you can check whether or not to cancel. Like:
void DoWork() {
var commands = new Queue<ICommand>();
commands.Enqueue(new MoreWorkCommand());
while (!isStopping && !commands.IsEmpty)
{
commands.Deque().Perform(commands);
}
}
public class MoreWorkCommand : ICommand {
public void Perform(Queue<ICommand> commands) {
commands.Enqueue(new DoMoreWorkCommand());
}
}
Basically, by turning the low-level callstack into a data structure you control, you have the ability to check stuff between each "call", pause, resume, cancel, etc..

Categories

Resources