任务并行库的优化实现和使用?

时间:2013-07-12 20:16:20

标签: c# wcf asynchronous task-parallel-library task

我有一个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和异步工作,我不知道如何才能做到这一点,是否值得改进。

1 个答案:

答案 0 :(得分:2)

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

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

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