1

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)

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
morgelo
  • 136
  • 15

3 Answers3

2

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;
}
Community
  • 1
  • 1
Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • Thanks your solution seems pretty good. One Question to the linked page: every time I call the FromEvent() function there is added a new function to the event. Do I have to remove this afterwards or is the Garbage-collector doing this for me. In my case this function could be called very often. – morgelo Jan 29 '15 at 09:51
2

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

Patrick
  • 17,669
  • 6
  • 70
  • 85
0

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.

DrKoch
  • 9,556
  • 2
  • 34
  • 43
  • `100000 * 100` iterations for busy wait? You're probably planning to burn the cpu just for the sake of waiting? – Sriram Sakthivel Jan 29 '15 at 09:38
  • 1
    On a 1Ghz CPU this will take 100k * 100 = 10M cycles == 100ms (the actual numbers needed will vary...) – DrKoch Jan 29 '15 at 10:02
  • Use the built-in implementation of [SpinWait](https://learn.microsoft.com/en-us/dotnet/standard/threading/spinwait) instead. Do not reinvent the wheel, especially not with such hard-coded values. – Bip901 Jan 18 '22 at 11:28