Reputation: 146
I'm working on a Problem on an existing Project. We want to read an ADC-value and normally we use there a fire-and-forget concept. We ask for the value and after the value is read an event is raised. However now I have to implement a function, which returns a value directly. My idea was to solve this Problem with polling.
public class Information
{
public delegate void NewADCValueArrived(double newValue);
private event NewADCValueArrived newAdcValue;
private double getADCValueDirectly()
{
double value = -1;
NewADCValueArrived handler = delegate(double newValue)
{
value = newValue;
};
newAdcValue += handler;
askFornewValues(); //Fire and forget
timeout = 0;
while(value != -1 && timeout <100)
{
timeout++;
Thread.sleep(1); //Want to avoid this!! because sleeping for 1 ms is very inaccurate
}
newAdcValue -= handler;
if (value != -1)
{
return value;
}
else
{
throw Exception("Timeout");
}
}
}
The problem now is, that I want to avoid polling. Because often the response is even faster than 1 ms and I want to finish the function as fast as possible. Do you have a better idea to solve this Problem?
In the c#-Documentation I found some information about WaitHandlers but I was not able to integrate them into my program. (https://msdn.microsoft.com/en-us/library/system.threading.waithandle)
Upvotes: 1
Views: 2056
Reputation: 73492
You can use TaskCompletionSource
to abstract the event as a Task
. You can refer this question for how to do that. You don't even need to refer the answers; question itself shows how.
Once you get the Task
you don't have to poll anymore. You can do all sort of interesting things like Wait
, ContinueWith
or even await
.
For the timeout you can use call TaskCompletionSource.SetCanceled
with a Timer
.
As to how to unsubscribe from the event:(asked in comments)
public class MyClass
{
public event Action OnCompletion;
}
public static Task FromEvent(MyClass obj)
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
Action completion = null;
completion = () =>
{
tcs.SetResult(null);
obj.OnCompletion -= completion;
};
obj.OnCompletion += completion;
return tcs.Task;
}
Upvotes: 2
Reputation: 9782
If its really realtime and you can't afford to let the Dispatcher take action, you could just do a busy wait:
timeout = 0;
while(value != -1 && timeout <100000)
{
timeout++;
for(int j= 0; j < 100; j++); // keep CPU busy
}
This assumes that your value
is modified by another thread and that you allow the program to freeze for a short period of time. On the plus side no windows dispatching (Task, Events, etc) crosses your way.
Upvotes: 0
Reputation: 17973
If you have the option I would probably go with a task based solution.
Otherwise, what you can do is set up an AutoResetEvent and wait for that to be triggered by the event handler.
private double getADCValueDirectly()
{
double value = -1;
AutoResetEvent eventCompleted = new AutoResetEvent(false);
NewADCValueArrived handler = delegate(double newValue)
{
value = newValue;
// signal the waithandle
eventCompleted.Set();
};
newAdcValue += handler;
askFornewValues(); //Fire and forget
eventCompleted.WaitOne(); // optionally enter a timeout here
newAdcValue -= handler;
if (value != -1)
{
return value;
}
else
{
throw Exception("Timeout");
}
}
There's an excellent tutorial on C# thread handling at http://www.albahari.com/threading/part2.aspx#_AutoResetEvent
Upvotes: 2