【问题标题】:Optimal Implementation and Usage of Task Parallel Library?任务并行库的优化实现和使用?
【发布时间】:2013-07-12 20:16:20
【问题描述】:

我有一个 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 和工作异步的内容,但我不确定如何才能使其适应以及是否值得改进。

【问题讨论】:

  • .NET 4.5 是一种选择吗?
  • 这是在不久的将来的一个选项,但不确定多近! :)
  • 您始终可以使用适用于 .NET 4 的 Bcl 异步目标包 - async/await 在这里确实是正确的方法,而不是通过新线程/任务的 TPL。

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


【解决方案1】:

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

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

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

【讨论】:

  • 我一直在尝试确定是否值得进行此更改,但是我仍然不确定我将如何完成我目前使用 Task.WaitAll(...) 与超时特定如果我使用的是 async/await?
  • 我阅读了一些可能可以使用 Task.Delay() 的地方,但不确定。
  • @CitadelCSAlum 基本上,使用 async/await,这可以不使用线程来完成。现在,您正在为每个请求使用一个线程,这是一个巨大的线程数。效率会大大提高。
  • 我仍然很困惑我如何能够以 35 秒的超时时间完成上述代码,并在 35 秒后让返回的异步全部在一行执行中继续执行?
  • @CitadelCSAlum 请参阅 stackoverflow.com/questions/9846615/… 了解如何使用 Task.WhenAll 超时
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多