【问题标题】:Launching multiple tasks from a WCF service从 WCF 服务启动多个任务
【发布时间】:2011-01-17 22:27:04
【问题描述】:

我需要优化 WCF 服务...这是一件相当复杂的事情。这次我的问题与任务有关(任务并行库,.NET 4.0)。发生的情况是我在调用服务时启动了几个任务(使用Task.Factory.StartNew),然后等待它们完成:

Task.WaitAll(task1, task2, task3, task4, task5, task6);

好的...我看到但不喜欢的是,在第一次调用时(有时是前 2-3 次调用,如果一个接一个地快速进行),最后的任务开始比其他任务晚得多(我正在研究一个比其他人晚 0.5 秒开始的案例)。我试着打电话

ThreadPool.SetMinThreads(12*Environment.ProcessorCount, 20);

在我的服务开始时,但它似乎没有帮助。

这些任务都是与数据库相关的:我正在从多个数据库中读取数据,它必须花费尽可能少的时间。

知道为什么最后一个任务需要这么长时间吗?有什么我可以做的吗?

或者,我应该直接使用线程池吗?碰巧的是,在我正在查看的一种情况下,一个任务在最后一个任务开始之前已经结束 - 如果我重用该线程而不是等待创建新线程,我会节省 0.2 秒。但是,我不能确定 那个 任务总是会这么快结束,所以我不能将两个请求放在同一个任务中。

[编辑] 操作系统是 Windows Server 2003,所以应该没有连接限制。此外,它托管在 IIS 中 - 我不知道我应该创建常规线程还是使用线程池 - 这是首选版本?

[编辑] 我也尝试过使用Task.Factory.StartNew(action, TaskCreationOptions.LongRunning); - 它没有帮助,最后一个任务仍然比其他任务开始晚得多(大约半秒后)。

[编辑] MSDN1 说:

线程池有内置延迟 (.NET Framework 中的半秒 2.0 版)在开始新的空闲之前 线程。如果您的应用程序 定期启动许多任务 时间短,小幅增长 空闲线程的数量可以产生一个 吞吐量显着增加。 也设置空闲线程的数量 高消耗系统资源 不必要的。

但是,正如我所说,我已经在调用 SetMinThreads,但它没有帮助。

【问题讨论】:

  • +1 因为两年前我遇到了同样的问题,我对我的解决方案不满意。希望有人能给出一个好的答案:)
  • 嗨,从其他 cmets 和您的问题来看,尚不清楚 5 个 DB 是在 5 个单独的盒子上还是都在 1 个 SQL 服务器实例上?此外,每个 WCF 调用 5 的 DB 调用的确切数量是多少,或者如果它不同,那么平均值是多少。产生的呼叫范围?我想创建一个测试并希望尽可能精确。
  • 对不起,我直到现在才看到你的评论。多个盒子,不要以为有5个,但肯定不止一个。每个服务调用恰好有 6 个 DB 调用(我正在组合数据);其中两个调用是针对同一个数据库的。 (我正在我们的电话数据库中搜索家庭电话和工作电话。)

标签: c# multithreading task-parallel-library


【解决方案1】:

我自己在使用 (.Net 4.0) 任务对象时遇到了线程启动延迟的问题。所以对于时间关键的东西,我现在使用专用线程(......再次,因为这是我在 .Net 4.0 之前所做的。)

线程池的目的是避免启动和停止线程的操作系统成本。线程只是被重用。这是在例如 Internet 服务器中找到的常见模型。优点是他们可以更快地响应。

我编写了许多应用程序,通过让专用线程从任务队列中提取任务来实现我自己的线程池。但是请注意,这通常需要锁定,这可能会导致延迟/瓶颈。这取决于您的设计;如果任务很小,那么会有很多锁定,用一些 CPU 换取更少的锁定可能会更快:http://www.boyet.com/Articles/LockfreeStack.html

SmartThreadPool 是 .Net 线程池的替换/扩展。正如您在此链接中看到的,它有一个不错的 GUI 可以进行一些测试:http://www.codeproject.com/KB/threads/smartthreadpool.aspx

最终,这取决于您的需求,但为了获得高性能,我建议您实现自己的线程池。如果您遇到大量线程空闲,那么增加线程数(超出推荐的 cpucount*2)可能会有所帮助。这实际上是超线程在 CPU 内部的工作方式 - 在执行操作时使用“空闲”时间来执行其他操作。

请注意,.Net 对每个进程有 25 个线程的内置限制(即,对于您同时收到的所有 WCF 调用)。此限制是独立的,会覆盖 ThreadPool 设置。它可以增加,但需要一些魔法:http://www.csharpfriends.com/Articles/getArticle.aspx?articleID=201

【讨论】:

  • 谢谢......我下载了这个(SmartThreadPool)并尝试了......同样的事情发生了。我仍在试图弄清楚这该死的事情......这不是什么大不了的事(在某些情况下它不会为我节省任何时间,因为最后一个线程并不总是最后完成),但这真的让我很烦。
  • 应用于 .NET 1 的 25 个线程 - .NET 4 没有固定限制,但取决于各种因素 - 请参阅 msdn.microsoft.com/en-us/library/… 您可以更改顶部的版本以返回历史上。
【解决方案2】:

根据我之前的问题(是的,应该是针对原始消息的 Q - 道歉):

为什么您认为为机器中的每个处理器内核创建 12 个线程会在某种程度上加快服务器创建工作线程的能力?你所做的只是降低你的服务器速度!

按照 MSDN 做的

根据 MSDN 文档:“您可以使用 SetMinThreads 方法来增加最小线程数。但是,不必要地增加这些值会导致性能问题。如果太多任务同时启动有时,它们可能看起来都很慢。在大多数情况下,线程池使用其自己的分配线程的算法会表现得更好。将最小值减少到小于处理器数量也会损害性能。"。

此类问题通常是由于遇到限制或对共享资源的争用造成的。

在您的情况下,我猜您的最后一个任务在等待与数据库服务器的连接可用或数据库响应时被阻塞。请记住 - 如果您的调用启动了 5-6 个其他任务,那么您的机器将不得不创建和打开大量数据库连接,并且可能会通过大量工作来启动数据库。如果您的 WCF 服务器和/或数据库服务器很冷,那么在机器的缓存等被填充之前,您的前几次调用将会变慢。

您是否尝试过使用秒表添加一些跟踪/日志记录来计算您的任务连接到数据库服务器然后执行其操作所需的时间?

您可能会发现,减少启动的并发任务数量实际上会加快处理速度。尝试一次生成 3 个任务,等待它们完成,然后生成下一个 3。

【讨论】:

  • 1.有多个数据库——准确地说是 5 个。 2. 由于线程被阻塞,它们不应该导致性能问题。他们在那段时间没有使用 CPU。 3. 是的,我对所有事情都有详细的时间安排……打开与数据库的连接只需要百分之几秒。 4.不,减少并发任务的数量不会加快速度......无论如何 5.我在这个问题中试图解决的唯一问题是:为什么线程启动这么晚,我该怎么做它与其他人同时开始?
【解决方案3】:

当您调用Task.Factory.StartNew 时,它使用TaskScheduler 将这些任务映射到实际工作项。

在您的情况下,听起来您的一项任务偶尔会延迟,而操作系统会为工作项启动一个新线程。您可以潜在地构建一个custom TaskScheduler,它已经包含六个处于等待状态的线程,并明确地将它们用于这六个任务。这将使您能够完全控制这些初始任务的创建和启动方式。

话虽如此,我怀疑这里还有其他东西在起作用...您提到使用TaskCreationOptions.LongRunning 展示了相同的行为。这表明还有其他一些因素导致了这半秒的延迟。我怀疑这是由于TaskCreationOptions.LongRunning 的性质造成的 - 当使用默认的TaskScheduler(LongRunningTaskScheduler 类使用的提示)时,使用TaskCreationOptions.LongRunning 启动任务实际上会创建一个全新的(非-ThreadPool) 该任务的线程。如果创建 6 个任务,全部使用 TaskCreationOptions.LongRunning,表现出相同的行为,您几乎可以保证问题不是默认的 TaskScheduler,因为这将始终手动启动 6 个线程。

我建议通过性能分析器运行您的代码,并可能在 VS 2010 中使用并发可视化工具。这应该可以帮助您准确确定导致半秒延迟的原因。

【讨论】:

    【解决方案4】:

    什么是操作系统?如果您没有运行 Windows 的服务器版本,则存在连接限制。由于连接限制,您的许多线程可能正在序列化。

    另外,我还没有使用任务并行库,但我有限的经验是在网络环境中创建新线程很便宜。

    【讨论】:

    • 不,它是服务器 - Windows Server 2003。
    【解决方案5】:

    这些文章可能会解释您遇到的问题: http://blogs.msdn.com/b/wenlong/archive/2010/02/11/why-are-wcf-responses-slow-and-setminthreads-does-not-work.aspx

    http://blogs.msdn.com/b/wenlong/archive/2010/02/11/why-does-wcf-become-slow-after-being-idle-for-15-seconds.aspx

    看到您正在使用 .Net 4,第一篇文章可能不适用,但第二篇文章指出 ThreadPool 在 15 秒后终止空闲线程,这可能解释了您遇到的问题并提供了一个简单的(虽然有点 hacky)解决它的方法。

    您是否应该直接使用 ThreadPool 不会有任何区别,因为我怀疑任务库无论如何都在为您使用它。

    我们已经使用了一段时间的第三方库可能会在这里为您提供帮助 - Smart Thread Pool。您仍然可以获得使用任务库的相同好处,因为您可以从线程获取返回值并从它们获取任何异常信息。

    另外,你可以实例化线程池,这样当你有多个地方都需要一个线程池时(这样低优先级的进程就不会开始占用一些高优先级进程的配额)哦,是的,你可以设置优先级池中的线程也是您无法使用所有线程都是后台线程的标准 ThreadPool 完成的。

    您可以在 codeplex 页面上找到大量信息,我也有一篇文章重点介绍了一些关键差异: http://theburningmonk.com/2010/03/threading-introducing-smartthreadpool/

    顺便说一句,对于您提到的任务,可能需要一些时间才能返回,您可能无论如何都不应该使用线程池。建议我们应该避免将线程池用于任何类似的阻塞任务,因为它会占用框架类的各种事物使用的线程池,例如处理计时器事件等(更不用说处理传入的 WCF要求!)。我觉得我在这里发送垃圾邮件,但这里是我收集的一些关于线程池使用的信息以及底部的一些有用链接:

    http://theburningmonk.com/2010/03/threading-using-the-threadpool-vs-creating-your-own-threads/

    嗯,希望对你有帮助!

    【讨论】:

      猜你喜欢
      • 2017-02-13
      • 2011-01-25
      • 2013-06-21
      • 1970-01-01
      • 2011-06-02
      • 2011-04-21
      • 1970-01-01
      相关资源
      最近更新 更多