努力搜索一段符合我想要的代码,我很满意。阅读this和this有很多帮助。
我有一个场景,我需要单个消费者在新数据可用时通知单个生产者,但也希望定期通知消费者,无论新数据是否可用。 如果通知消费者的时间超过了重复发生的时间,那就没有问题,但不应该频繁通知消费者。
当消费者已经通知并正在工作时,可能会发生“新数据”的多个通知。 (所以SemaphoreSlim
并不合适。
因此,比生产者通知速度慢的消费者不会排队后续通知,他们只会“重新发出信号”相同的“数据可用”标志而不受影响。
我还希望消费者异步等待通知(不阻塞线程)。
我将下面的类包裹在TaskCompletionSource
并使用内部Timer。
public class PeriodicalNotifier : IDisposable
{
// Need some dummy type since TaskCompletionSource has only the generic version
internal struct VoidTypeStruct { }
// Always reuse this allocation
private static VoidTypeStruct dummyStruct;
private TaskCompletionSource<VoidTypeStruct> internalCompletionSource;
private Timer reSendTimer;
public PeriodicalNotifier(int autoNotifyIntervalMs)
{
internalCompletionSource = new TaskCompletionSource<VoidTypeStruct>();
reSendTimer = new Timer(_ => Notify(), null, 0, autoNotifyIntervalMs);
}
public async Task WaitForNotifictionAsync(CancellationToken cancellationToken)
{
using (cancellationToken.Register(() => internalCompletionSource.TrySetCanceled()))
{
await internalCompletionSource.Task;
// Recreate - to be able to set again upon the next wait
internalCompletionSource = new TaskCompletionSource<VoidTypeStruct>();
}
}
public void Notify()
{
internalCompletionSource.TrySetResult(dummyStruct);
}
public void Dispose()
{
reSendTimer.Dispose();
internalCompletionSource.TrySetCanceled();
}
}
此类的用户可以执行以下操作:
private PeriodicalNotifier notifier = new PeriodicalNotifier(100);
// ... In some task - which should be non-blocking
while (some condition)
{
await notifier.WaitForNotifictionAsync(_tokenSource.Token);
// Do some work...
}
// ... In some thread, producer added new data
notifier.Notify();
效率对我很重要,场景是高频数据流,所以我想到了:
TaskCompletionSource
我的问题是:
答案 0 :(得分:0)
TaskCompletionSource
娱乐应该在使用范围之外,否则“旧”取消令牌可能取消“新”TaskCompletionSource
。AsyncManualResetEvent
结合Timer
会更简单,更不容易出错。在Visual Studio SDK by Microsoft中有一个非常好的名称空间和异步工具。您需要install the SDK然后引用Microsoft.VisualStudio.Threading
程序集。以下是使用AsyncManualResetEvent
使用相同API的实现:
public class PeriodicalNotifier : IDisposable
{
private readonly Timer _timer;
private readonly AsyncManualResetEvent _asyncManualResetEvent;
public PeriodicalNotifier(TimeSpan autoNotifyInterval)
{
_asyncManualResetEvent = new AsyncManualResetEvent();
_timer = new Timer(_ => Notify(), null, TimeSpan.Zero, autoNotifyInterval);
}
public async Task WaitForNotifictionAsync(CancellationToken cancellationToken)
{
await _asyncManualResetEvent.WaitAsync().WithCancellation(cancellationToken);
_asyncManualResetEvent.Reset();
}
public void Notify()
{
_asyncManualResetEvent.Set();
}
public void Dispose()
{
_timer.Dispose();
}
}
通过设置重置事件进行通知,使用WaitAsync
异步等待,使用WithCancellation
扩展方法启用取消,然后重置事件。通过设置相同的重置事件来“合并”多个通知。
答案 1 :(得分:0)
Subject<Result> notifier = new Subject<Result)();
notifier
.Select(value => Observable.Interval(TimeSpan.FromMilliSeconds(100))
.Select(_ => value)).Switch()
.Subscribe(value => DoSomething(value));
//Some other thread...
notifier.OnNext(...);
此Rx查询将每隔100毫秒继续发送值,直到新值出现。然后我们每100毫秒通知一次该值。
如果我们每100毫秒收到一次超过一次的值,那么我们的输出基本上与输入相同。