2

WebベースのAPIにリクエストを送信するコンポーネントがありますが、APIのデータ制限に違反しないように、これらのリクエストを調整する必要があります。つまり、すべての要求は、送信される速度を制御するためにキューを通過する必要がありますが、最大のスループットを達成するために同時に実行できます(実行する必要があります)。各リクエストは、完了した将来のある時点で、呼び出し元のコードにデータを返す必要があります。

データの返送を処理するための優れたモデルを作成するのに苦労しています。

エンキュープロセスとデキュープロセスはバッファの両端にあるため、Iを使用してメソッドからBlockingCollectionaを返すことはできません。その代わりに、フォームのコールバックを含む型を作成します。Task<TResult>ScheduleRequestItem<TResult>Action<Task<TResult>>

アイテムがキューからプルされると、開始されたタスクでコールバックを呼び出すことができますが、その時点でジェネリック型パラメーターを失い、リフレクションとあらゆる種類の厄介さを使用したままになります(可能です)。

例えば:

public class RequestScheduler 
{
    private readonly BlockingCollection<IRequestItem> _queue = new BlockingCollection<IRequestItem>();

    public RequestScheduler()
    {
        this.Start();
    }

    // This can't return Task<TResult>, so returns void.
    // Instead RequestItem is generic but this poses problems when adding to the queue
    public void Schedule<TResult>(RequestItem<TResult> request)
    {
        _queue.Add(request);
    }

    private void Start()
    {
        Task.Factory.StartNew(() =>
        {
            foreach (var item in _queue.GetConsumingEnumerable())
            {
                // I want to be able to use the original type parameters here
                // is there a nice way without reflection?
                // ProcessItem submits an HttpWebRequest
                Task.Factory.StartNew(() => ProcessItem(item))
                   .ContinueWith(t => { item.Callback(t); });
            }
        });
    }

    public void Stop()
    {
        _queue.CompleteAdding();
    }
}

public class RequestItem<TResult> : IRequestItem
{
    public IOperation<TResult> Operation { get; set; }
    public Action<Task<TResult>> Callback { get; set; }
}

Task<TResult>リクエストをバッファリングし続けても、リクエストがバッファからプルされてAPIに送信されたときにクライアントに返すにはどうすればよいですか?

4

1 に答える 1

2

まず、から戻ることができます。そのために使用する必要があります。Task<TResult>Schedule()TaskCompletionSource

次に、一般性の問題を回避するために、すべてを(非一般的な)Actionの中に隠すことができます。でSchedule()、必要なことを正確に実行するラムダを使用してアクションを作成します。消費ループはそのアクションを実行します。内部に何があるかを知る必要はありません。

Task第三に、ループの各反復でなぜ新しいものを開始するのかわかりません。一つには、それはあなたが実際にスロットルを取得しないことを意味します。

これらの変更により、コードは次のようになります。

public class RequestScheduler
{
    private readonly BlockingCollection<Action> m_queue = new BlockingCollection<Action>();

    public RequestScheduler()
    {
        this.Start();
    }

    private void Start()
    {
        Task.Factory.StartNew(() =>
        {
            foreach (var action in m_queue.GetConsumingEnumerable())
            {
                action();
            }
        }, TaskCreationOptions.LongRunning);
    }

    public Task<TResult> Schedule<TResult>(IOperation<TResult> operation)
    {
        var tcs = new TaskCompletionSource<TResult>();

        Action action = () =>
        {
            try
            {
                tcs.SetResult(ProcessItem(operation));
            }
            catch (Exception e)
            {
                tcs.SetException(e);
            }
        };

        m_queue.Add(action);

        return tcs.Task;
    }

    private T ProcessItem<T>(IOperation<T> operation)
    {
        // whatever
    }
}
于 2013-03-19T22:12:10.473 に答える