等待事件 - 比投票更好的解决方案?

时间:2015-01-29 09:17:55

标签: c# events wait

我正在研究现有项目的问题。我们想要读取ADC值,通常我们在那里使用一个“一劳永逸”的概念。我们要求输入值,并在读取值后引发事件。但是现在我必须实现一个直接返回值的函数。我的想法是用轮询来解决这个问题。

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");
        }
    }
}

现在的问题是,我想避免轮询。因为响应通常甚至超过1毫秒,我想尽快完成功能。 你有更好的想法来解决这个问题吗?

在c#-Documentation中,我找到了一些关于WaitHandlers的信息,但是我无法将它们集成到我的程序中。 (https://msdn.microsoft.com/en-us/library/system.threading.waithandle

3 个答案:

答案 0 :(得分:2)

您可以使用TaskCompletionSource将事件抽象为Task。您可以参考this question了解如何执行此操作。你甚至不需要参考答案;问题本身就说明了如何。

一旦获得Task,您就不必再进行投票了。您可以执行各种有趣的操作,例如WaitContinueWith甚至await

对于超时,您可以使用TaskCompletionSource.SetCanceled来调用Timer

关于如何取消订阅活动:(在评论中提问)

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;
}

答案 1 :(得分:2)

如果你有选择我可能会选择基于任务的解决方案。

否则,你可以做的是设置一个AutoResetEvent并等待事件处理程序触发它。

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");
    }
}

http://www.albahari.com/threading/part2.aspx#_AutoResetEvent

上有一个关于C#线程处理的优秀教程

答案 2 :(得分:0)

如果真的实时并且您无法让Dispatcher采取行动,那么您可以执行忙碌等待

    timeout = 0;
    while(value != -1 && timeout <100000)
    {
        timeout++;
        for(int j= 0; j < 100; j++); // keep CPU busy
    }

这假设您的value被另一个线程修改,并且您允许程序在短时间内冻结。在正面,没有窗户调度(任务,事件等)穿过你的方式。