1

我有一个 WCF 服务,它可以响应报价并“伸出”并动态地将此报价提供给 X 数量的潜在买家(通常是 15-20),这些潜在买家本质上是外部 API。

每个买家目前有 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*/

平均而言,每天调用此服务的次数为 400K -900K,有时每秒高达 30-40 次。

为了调整性能,我们进行了很多优化,但我想确保这段代码没有任何明显的问题。

我阅读了很多关于 TaskScheduler 的强大功能以及弄乱 SynchronizationContext 和工作异步的内容,但我不确定如何才能使其适合以及是否值得改进。

4

1 回答 1

2

现在,您正在使用线程池线程(每个Task.Factory.StartNew调用都使用一个 TP 线程或一个完整的 .NET 线程,在您的情况下,由于LongRunning提示)用于有效地受 IO 限制的工作。如果您没有指定TaskCreationOptions.LongRunning,那么您很早就看到了问题,并且您将遇到线程池饥饿。实际上,您可能会使用大量线程,并且非常快速地创建和销毁它们,这是对资源的浪费。

如果您要使其完全异步,并使用新的 async/await 支持,您可以异步执行相同的“工作”,而无需使用线程。这将显着更好地扩展,因为用于给定数量请求的线程数量将显着减少。

作为一般经验法则,Task.Factory.StartNew(或Task.Run在 .NET 4.5 中,以及Parallel类)应仅用于 CPU 绑定工作,而async/await应用于 IO 绑定工作,尤其是服务器端操作。

于 2013-07-12T20:18:36.240 回答