这是我的方法。我有几个IEventProviders
,
interface IEventProvider
{
Task<Event> GetEvent();
}
然后我有一个容器类来包装它们,并继续调用并等待GetEvent()
等待下一个事件,例如套接字异步接收,定时器滴答等
class EventProviderContainer : IEventProvider
{
private IEventProvider[] _providers;
private Task<Event>[] _tasks;
public EventProviderContainer(params IEventProvider[] providers)
{
_providers = providers;
}
public async Task<Event> GetEvent()
{
// Fill the _tasks first time we call the method.
if (_tasks == null)
_tasks = (from p in _providers select p.GetEvent()).ToArray();
Task<Event> task = await Task<Event>.WhenAny(_tasks);
// Get the provider index whose previous task is done.
int index = Array.IndexOf(_tasks, task);
// put next event of the provider into array.
_tasks[index] = _providers[index].GetEvent();
return await task;
}
}
我认为这有点难看。这是一个更好的方法吗?
答案 0 :(得分:2)
对于一项实际上并不那么简单的任务,您的代码非常简短且易于理解,而且我个人认为它并不丑陋。
除非您想要更改整个界面,否则我认为您无法找到更好的编写此代码的方法。我唯一要改变的是将_tasks
初始化为构造函数(但也许你有理由)。
但我同意斯蒂芬的评论,对于事件,使用“推”语义通常比“拉”更合适。为此,Rx(IObservable<Event>
)或TPL数据流(ISourceBlock<Event>
)将非常有用。在这两种情况下,编写EventProviderContainer
都会相对简单。两者中哪一个是更好的选择取决于你将如何处理结果。
答案 1 :(得分:1)
如果您希望每次为每个提供商提供一个活动,那么我建议您查看包含Interleaved
方法的Processing tasks as they complete MSDN文章。此方法接受一组任务并返回一个新的任务数组,这些任务将按顺序完成。
另一方面,如果您希望在每个提供商到达时不断接收事件,那么我建议您查看Microsoft的Reactive Extensions (Rx)项目。
使用Rx,您的事件提供程序界面将变为:
public interface IEventProvider
{
IObservable<Event> OnEvent();
}
然后,您的容器提供程序将使用Observable.Merge扩展方法来组合每个子提供程序的事件。
return _providers.Select(provider => provider.OnEvent()).Merge();
要实际接收事件,您可以通过附加每次新事件可用时执行的回调委托来订阅observable。
var provider = new EventProviderContainer(
new TestEventProvider("a", 1000),
new TestEventProvider("b", 1300),
new TestEventProvider("c", 1600));
provider.OnEvent().Subscribe(Console.WriteLine);
Console.ReadLine();
以上示例使用测试事件提供程序,该提供程序使用Observable.Timer扩展方法以给定的时间段(以毫秒为单位)返回连续的事件流。
return Observable.Timer(TimeSpan.Zero, TimeSpan.FromMilliseconds(_period))
.Select(i => new TestEvent(_name, i));
答案 2 :(得分:0)
我认为您要实现的目标的正确代码是:
public async Task<Event> GetEvent()
{
// Fill the _tasks first time we call the method.
if (_tasks == null)
_tasks = (from p in _providers select p.GetEvent()).ToArray();
return await await Task<Event>.WhenAny(_tasks);
}
await await
似乎有点奇怪,但由于WhenAny()
返回Task<Task<T>>
,它必须是正确的。