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をコンストラクターに移動することです (ただし、その理由があるかもしれません)。

しかし、イベントの場合、通常は「プル」よりも「プッシュ」セマンティクスを使用する方が適切であるという Stephen のコメントに同意します。そのためには、Rx ( IObservable<Event>) または TPL Dataflow ( ISourceBlock<Event>) が非常に役立ちます。どちらの場合も、書き込みEventProviderContainerは比較的簡単です。2 つのうちどちらがより適切な選択であるかは、結果をどのように処理するかによって異なります。

于 2013-01-14T19:22:26.793 に答える
1

一度にプロバイダーごとに1つのイベントが必要な場合は、メソッドを含むMSDNの記事を完了するため、処理タスクInterleavedを確認することをお勧めします。このメソッドは、タスクのコレクションを受け取り、完了順に生成されるタスクの新しい配列を返します。

一方、各プロバイダーからイベントが到着したときに継続的に受信する場合は、MicrosoftのReactive Extensions(Rx)プロジェクトを確認することをお勧めします。

Rxを使用すると、イベントプロバイダーインターフェイスは次のようになります。

public interface IEventProvider
{
    IObservable<Event> OnEvent();
}

次に、コンテナプロバイダーは、Observable.Merge拡張メソッドを使用して、各子プロバイダーのイベントを結合します。

return _providers.Select(provider => provider.OnEvent()).Merge();

実際にイベントを受信するには、新しいイベントが利用可能になるたびに実行されるコールバックデリゲートをアタッチすることにより、オブザーバブルをサブスクライブします。

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

await await少し奇妙に思えWhenAny()ますが、 a を返すので、Task<Task<T>>正しいに違いありません。

于 2013-01-11T10:08:36.763 に答える