Reputation: 122112
I am trying to implement the following functionality:
class WeightResolver {
WeightMonitor _source;
bool _cancelled;
Weight _threshold;
public Cancel() {
_cancelled = true;
}
public Weight Resolve(){
_cancelled = false;
while(_source.CurrentWeight < threshold ) {
if(_cancelled)
throw new CancelledOperationException();
// Wait until one of the above conditions is met
}
return _source.CurrentWeight
}
}
However I am running into trouble managing my threads. For example, the Cancel method is registered via an event and Resolve invoked as follows:
_activity_timeout_manager.TimeoutHandler += new Action(_weight_resolver.Cancel())l
try {
var weight = _weight_resolver.Resolve();
}
catch(CancelledOperationException) { .... }
where the activity manager is running a timer on tick of which it invokes events using TimeoutHandler.Invoke();
The problem is that even though it is properly registered with the event, Cancel() never gets called. I believe this is because the thread it is calling to is currently spinning and therefore it never gets a chance at the CPU.
What can I do to remedy the situation short of making the call to Resolve() asynchronous? It is extremely preferable for WeightResolver.Resolve() to stay synchronous because the code calling it should spin unless some return is provided anyways.
EDIT: To clarify what I'm asking for. This seems like a fairly common set-up and I would be surprised if there isn't a simple standard way to handle it. I simply have never run across the situation before and don't know what exactly it could be.
Upvotes: 1
Views: 844
Reputation: 14788
What you need here are condition variables. I don't know specifically for .NET but in general you will have something like this
Condition cond;
Mutex lock;
public Cancel() {
lock.lock()
_cancelled = true;
cond.signal(lock);
lock.unlock();
}
public Weight Resolve(){
_cancelled = false;
lock.lock();
while(_source.CurrentWeight < threshold) {
if(_cancelled)
{
lock.unlock();
throw new CancelledOperationException();
}
cond.timedWait(lock, 100);
// Wait until one of the above conditions is met
}
lock.unlock();
return _source.CurrentWeight
}
even better would be if your WeightResolver signalled on the same condition when the weight changed. e.g.
Condition cond;
Mutex lock;
public Cancel() {
lock.lock()
_cancelled = true;
cond.signal(lock);
lock.unlock();
}
public Weight Resolve(){
_cancelled = false;
lock.lock();
while(_source.CurrentWeight < threshold) {
if(_cancelled)
{
lock.unlock();
throw new CancelledOperationException();
}
cond.wait(lock);
// Wait until one of the above conditions is met
}
lock.unlock();
return _source.CurrentWeight
}
and in the WeightMonitor Class, you had something like this.
public void updateWeight()
{
lock.lock();
...update weight;
cond.signal(lock);
lock.unlock();
}
where the Conditionvariable and lock are the same. in both classes.
This is a pretty standard use for condition variables, this is also how join is generally implemented.
Upvotes: 0
Reputation: 26109
You should be using a ManualResetEvent
instead of a bool
for canceling.
class WeightResolver {
WeightMonitor _source;
ManualResetEvent _cancelled = new ManualResetEvent(false);
Weight _threshold;
public Cancel() {
_cancelled.Set();
}
public Weight Resolve(){
_cancelled = false;
while(_source.CurrentWeight < threshold ) {
if(_cancelled.WaitOne(100))
throw new CancelledOperationException();
// Wait until one of the above conditions is met
}
return _source.CurrentWeight
}
}
Upvotes: 0
Reputation: 17610
Is this while loop ever giving up the CPU?
while(_source.CurrentWeight < threshold )
If not, then your inactivity timer won't get a chance to run. You might want to use ManualResetEvents (instead of the loop... have whatever sets _source.CurrentWeight set the event) or throw in a Thread.yield() every once in a while.
Upvotes: 0
Reputation: 21844
This might not work for you, but based on the information you've provided, I'd suggest looking at thread.Join(XXX) where XXX is the number of milliseconds to wait. It'll greatly simplify your code.
http://msdn.microsoft.com/en-us/library/6b1kkss0.aspx
you can block the calling thread to the new thread for a specified amount of time, after which you can abort the Resolve thread.
resolveThread.Start();
resolveThread.Join(2000); //this will block the main thread, thus making resolve synchronous
resolveThread.Abort(); //timeout has expired
Upvotes: 1