天天看点

RxJava 中的多线程RxJava 中的多线程

<b>本文讲的是RxJava 中的多线程,</b>

大多数情况下,我写的 Android 代码都是可以流畅运行的。直到上几周编写一个需要读取和分析大型文件的 app 之前,我从未关心过 app 运行速度的问题。

尽管我期望用户明白文件越大,耗时越长的道理,有时候他们仍会放弃我的应用。他们可能认为应用卡住了,也可能是因为他们就不想等那么久。所以如果我能把时间缩短至少一半的话,一定会大有裨益的。

因为我所有后台任务都用 RxJava 重写了,所以继续用 RxJava 来解决这个问题也是自然而然的。尤其是我还有一些如下所示的代码:

所以我只是想把循环的每个操作放到一个后台线程中。如下所示:

的确起作用了,时间减少了近一半。但也导致大量垃圾回收(GC),这使得加载时的 UI 又卡又慢。为了搞清楚问题的原因,我加了一句 log 打印如下信息<code>Thread.currentThread().getName()</code>。 这样我就搞清楚了,我在处理每一段数据时都新建了线程。正如结果所示,创建上千个线程并不是什么好主意。

我已经完成了加速数据处理的目标,但运行起来并不那么流畅。我想知道如果不触发这么多 GC 的话还能不能跑得再快点。所以我自己写了一个线程池并指定了最大线程数来供 RxJava 调用,省的每次处理数据都要创建新线程:

对于单个数据都很大的数据集来说,这样减少了约 10% 的数据处理时间。然而,对于单个数据都很小的数据集就减少了约 30% 的时间。同时也减少了 GC 的调用次数,但 GC 还是太频繁。

上文中我提到用两类数据集进行测试,一类的数据本身是大文件,但是数据集里包含的数据个数很少;另一类数据集里的每一个数据并不是很大,但是包含数据的总量很多。当我再次测试时,第一组数据几乎没差别,而第二组改变相当大。之前几乎要 20秒,现在只需 5秒。

第二类数据集运行时间改进了如此大的原因,是因为每个线程不再处理一个数据(而是处理一个从总体数据集里拆分下来的小数据集)。之前每一个数据,都需要调用一个线程来处理。现在我减少了调用线程的次数,从而提升了性能。

上面的代码要执行并发还有一些地方需要修改,所以我整理了代码并放到工具类中,使其更具有通用性。

这里 <code>T</code> 是被处理的数据类型,样例中是<code>DataModel</code>。传入待处理的 <code>List&lt;T&gt;</code> 并期望结果是<code>U</code>。在我的样例中 <code>U</code> 是 <code>List&lt;DataModel&gt;</code>,但它可以是任何东西,并不一定是一个 list。传入的回调函数负责数据子列表具体的业务处理并返回结果。

事实上影响运行速度的因素有许多。比如线程管理方式,线程数,设备等。大多数因素我无法控制,但总有一些是我没有考虑到的。

如果每个数据大小不相等会怎么样?举个例子,如果有 4 个线程,每个被指派给第 4 线程的数据大小是被指派给其他线程的十倍会怎么样?这时第四个线程的耗时就是其他线程的大约 10 倍。这种情况下使用多线程就不会减少多少时间。我的第二次尝试基本解决了这个问题,因为线程只在需要时才初始化。但这个方法太慢了。

我也试过改变数据分组方式。作为随意分配的取代,我可以跟踪每一组数据的总量,然后将数据分配给最少的那组。这样每个线程的工作量就接近平均了。倒霉的是,测试之后发现这样做增加的时间远大于它节省的时间。

数据被分配的大小越平均,处理速度就越快。但大多数情况下,随机分配看起来更快些。理想情况下是每个线程一有空就分配任务,同时执行分配所消耗的资源也少,这是最高效的。但我找不到一个足够高效的可以减少分配瓶颈的方法。

所以如果你想用多线程,这是我的建议。如果你有什么好想法,请务必告诉我。得到一个最优解(如果有的话)总是很难的。以及,能用多线程并不意味着必须用多线程。。

<b></b>

<b>原文发布时间为:2017年4月25日</b>

<b>本文来自云栖社区合作伙伴掘金,了解相关信息可以关注掘金网站。</b>