morgelo
morgelo

Reputation: 146

Wait for an Event - Better solution than polling?

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

Answers (3)

Sriram Sakthivel
Sriram Sakthivel

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

DrKoch
DrKoch

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

Patrick
Patrick

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

Related Questions