等待事件 - 比轮询更好的解决方案?
Wait for an Event - Better solution than polling?
我正在解决现有项目的问题。我们想要读取一个 ADC 值,通常我们会使用即发即弃的概念。我们询问该值,并在读取该值后引发一个事件。但是现在我必须实现一个函数,它 returns 直接是一个值。我的想法是通过轮询解决这个问题。
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)
您可以使用 TaskCompletionSource
将事件抽象为 Task
。您可以参考 this question 了解如何操作。您甚至不需要参考答案;问题本身说明了如何。
获得 Task
后,您无需再进行投票。您可以做各种有趣的事情,例如 Wait
、ContinueWith
甚至 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;
}
如果您可以选择,我可能会选择基于任务的解决方案。
否则,您可以设置一个 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# 线程处理的优秀教程
如果它真的是实时并且你不能让调度程序采取行动,你可以做一个忙等待:
timeout = 0;
while(value != -1 && timeout <100000)
{
timeout++;
for(int j= 0; j < 100; j++); // keep CPU busy
}
这假设您的 value
被另一个线程修改并且您允许程序短时间冻结。从好的方面来说,没有 windows 调度(任务、事件等)会影响你。
我正在解决现有项目的问题。我们想要读取一个 ADC 值,通常我们会使用即发即弃的概念。我们询问该值,并在读取该值后引发一个事件。但是现在我必须实现一个函数,它 returns 直接是一个值。我的想法是通过轮询解决这个问题。
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)
您可以使用 TaskCompletionSource
将事件抽象为 Task
。您可以参考 this question 了解如何操作。您甚至不需要参考答案;问题本身说明了如何。
获得 Task
后,您无需再进行投票。您可以做各种有趣的事情,例如 Wait
、ContinueWith
甚至 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;
}
如果您可以选择,我可能会选择基于任务的解决方案。
否则,您可以设置一个 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# 线程处理的优秀教程如果它真的是实时并且你不能让调度程序采取行动,你可以做一个忙等待:
timeout = 0;
while(value != -1 && timeout <100000)
{
timeout++;
for(int j= 0; j < 100; j++); // keep CPU busy
}
这假设您的 value
被另一个线程修改并且您允许程序短时间冻结。从好的方面来说,没有 windows 调度(任务、事件等)会影响你。