1

私は、オファーを受け取り、「手を差し伸べる」ことに応答可能なWCFサービスを持っており、本質的に外部APIである潜在的なバイヤーのX人(通常は15〜20人)にこのオファーを動的に提供します。

現在、各購入者は 35 秒以内に応答を返すか、オファーを購入できなくなります。

これを達成するために、私は次のコードを用意しました。これは 8 か月間運用されており、かなりうまく機能し、スケーリングされています。

最近、さらに拡張できるように改善に多くの時間を費やしているため、このタスクを達成するためのより良いオプションがあるかどうかに関心がありました. 今のところうまく機能しているので変更をためらっていますが、集中できるうちに追加のパフォーマンスを引き出すことができるかもしれません.

次のコードは、バイヤーへのアウトバウンド リクエストを作成するタスクを作成します。

IBuyer[] buyer = BuyerService.GetBuyers();  /*Obtain potential buyers for the offer*/
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Tasks = new Task<IResponse>[Buyers.Count];

for(int i = 0; i < Buyers.Count;i++)
{

    IBuyer buyer = Buyers[i];
    Func<IResponse> makeOffer = () => buyer.MakeOffer()
    Tasks[i] = Task.Factory.StartNew<IResponse>((o) =>
        {

            try
            {
                var result = MakeOffer();

                if (!token.IsCancellationRequested)
                {
                    return result;
                }
            } 
            catch (Exception exception
            {
                /*Do Work For Handling Exception In Here*/
            }
            return null;
        }, token,TaskCreationOptions.LongRunning);
};

Task.WaitAll(Tasks, timeout, token);    /*Give buyers fair amount of time to respond to offer*/
tokenSource.Cancel();

List<IResponse> results = new List<IResponse>();    /*List of Responses From Buyers*/

for (int i = 0; i < Tasks.Length; i++)
{
    if (Tasks[i].IsCompleted)   /*Needed so it doesnt block on Result*/
    {
        if (Tasks[i].Result != null)
        {
            results.Add(Tasks[i].Result);
        }
        Tasks[i].Dispose();
    }
}

/*Continue Processing Buyers That Responded*/

平均すると、このサービスは 1 日あたり 400K ~ 900K の範囲で呼び出され、1 秒あたり最大 30 ~ 40 回呼び出されることもあります。

パフォーマンスを調整するために多くの最適化を行いましたが、このコードに目立った問題がないことを確認したいと思います。

TaskScheduler のパワーと、SynchronizationContext をいじって非同期で動作することについて多くのことを読みましたが、それをどのように適合させることができるか、改善する価値があるかどうかはわかりません。

4

1 に答える 1

2

現在、事実上IOバウンドの作業にスレッドプールスレッドを使用しています(ヒントTask.Factory.StartNewのために、各呼び出しはTPスレッドまたは完全な.NETスレッドを使用します)。LongRunningを指定していなかった場合TaskCreationOptions.LongRunning、非常に早い段階で問題が発生し、スレッド プールの枯渇が発生していたでしょう。そのままでは、非常に多数のスレッドを使用し、それらを非常に迅速に作成および破棄する可能性が高く、これはリソースの浪費です。

これを完全に非同期にし、新しい async/await サポートを使用すると、スレッドを使用せずに同じ「作業」を非同期で実行できます。これにより、特定の数のリクエストに使用されるスレッドの量が大幅に削減されるため、スケーリングが大幅に向上します。

一般的な経験則として、Task.Factory.StartNew(またはTask.Run.NET 4.5 ではクラスと同様にParallel) は CPU バウンドの作業にのみ使用し、async/awaitは IO バウンドの作業、特にサーバー側の操作に使用する必要があります。

于 2013-07-12T20:18:36.240 に答える