0

我正在编写一个简单的实用程序,它接受一组可调用任务,并并行运行它们。希望总时间比最长任务所花费的时间少。该实用程序还添加了一些错误处理逻辑 - 如果任何任务失败,并且失败可以被视为“可重试”(例如超时或用户指定的异常),那么我们直接运行任务。

我已经围绕 ExecutorService 实现了这个实用程序。有两个部分:

  1. submit() 所有 Callable 任务到 ExecutorService,存储 Future 对象。
  2. 在 for 循环中,get() 每个 Future 的结果。如果出现异常,请执行“可重试”逻辑。

我编写了一些单元测试以确保使用此实用程序比按顺序运行任务更快。对于每个测试,我会生成一定数量的 Callable,每个基本上都会在一定范围内随机执行 Thread.sleep()。我尝试了不同的超时、不同数量的任务等,并且该实用程序似乎优于顺序执行。

但是当我将它添加到需要这种实用程序的实际系统中时,我看到的结果非常多变——有时并行执行更快,有时更慢,有时更快,但仍然比最长的个人任务。

我只是做错了吗?我知道 ExecutorService 有 invokeAll() 但这会吞噬底层异常。我还尝试使用 CompletionService 按完成顺序获取任务结果,但它表现出或多或少相同的行为。我现在正在阅读有关闩锁和障碍物的内容-这是解决此问题的正确方向吗?

4

2 回答 2

3

多线程编程不是免费的。它有一个开销。开销很容易超过和性能增益,通常会使您的代码更复杂。

额外的线程可以访问更多的 cpu 功率(假设你有多余的 cpu),但通常它们不会让你的 HDD 旋转得更快,给你更多的网络带宽或加速不受 cpu 限制的东西。

多个线程可以帮助您获得更大份额的外部资源。

于 2013-09-19T18:38:12.813 回答
3

我编写了一些单元测试以确保使用此实用程序比按顺序运行任务更快。对于每个测试,我会生成一定数量的 Callable,每个基本上都在一定范围内随机执行 Thread.sleep()

是的,这当然不是一个公平的测试,因为它既不使用 CPU 也不使用 IO。我当然希望并行睡眠比串行睡眠运行得更快。:-)

但是当我将它添加到需要这种实用程序的实际系统中时,我看到的结果非常多变

正确的。线程应用程序是否比串行应用程序运行得更快在很大程度上取决于许多因素。特别是,IO 绑定的应用程序不会提高性能,因为它们受 IO 通道的约束,因此实际上无法进行并发操作。应用程序需要的处理越多,将其转换为多线程的胜利就越大。

我只是做错了吗?

没有更多细节很难知道。您可能会考虑使用并发运行的线程数。如果你有大量的工作要处理,你不应该使用 aExecutos.newCachedThreadPool()并且应该newFixedSizeThreadPool(...)根据你的架构拥有的 CPU 数量来优化它。

您可能还想看看是否可以将 IO 操作隔离在几个线程中,并将处理隔离到其他线程。就像一个从文件读取的输入线程和一个输出线程(或一对)写入数据库之类的东西。因此,对于不同类型的任务,多个大小的池可能比使用单个线程池做得更好。

尝试使用 CompletionService 按完成顺序获取任务结果

如果您正在重试操作,使用 aCompletionService正是要走的路。当作业完成并抛出异常(或返回失败)时,它们可以重新启动并立即放回线程池中。我看不出您的性能问题会因此而产生的任何原因。

于 2013-09-19T18:32:20.593 回答