3

这是我的方法。我有几个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;
    }
}

我觉得有点丑。这是更好的方法吗?

4

3 回答 3

2

对于实际上并不那么简单的任务,您的代码非常简短且易于理解,而且我个人认为它并不难看。

我不认为你会找到更好的方法来编写这段代码,除非你想改变你的整个界面。我唯一要改变的是将初始化移动_tasks到构造函数(但也许你有这样做的理由)。

但我同意斯蒂芬的评论,对于事件,使用“推”语义通常比“拉”更合适。为此,Rx ( IObservable<Event>) 或 TPL Dataflow ( ISourceBlock<Event>) 将非常有用。在这两种情况下,写作EventProviderContainer都会相对简单。两者中哪一个是更好的选择取决于您将如何处理结果。

于 2013-01-14T19:22:26.793 回答
1

如果您希望一次为每个提供者提供一个事件,那么我建议您查看处理任务,因为它们完成了包含Interleaved方法的 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));
于 2013-01-11T10:02:53.120 回答
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);
    }

theawait await似乎有点奇怪,但由于WhenAny()返回 aTask<Task<T>>它必须是正确的。

于 2013-01-11T10:08:36.763 回答