【发布时间】:2017-04-10 01:49:11
【问题描述】:
我正在开发应用程序(Matt 的 traceroute Windows 版本 http://winmtr.net/),它创建多线程,每个线程都有自己的进程(执行 ping 命令)。 ThreadPoolExecutor 一段时间后关闭所有线程(例如 10 秒)
ThreadPoolExecutor 使用阻塞队列(在任务执行之前保持任务)
int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
NUMBER_OF_CORES * 2, NUMBER_OF_CORES * 2 + 2, 10L, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>()
);
PingThread.java
private class PingThread extends Thread {
@Override
public void run() {
long pingStartedAt = System.currentTimeMillis();
// PingRequest is custom object
PingRequest request = buildPingRequest(params);
if (!isCancelled() && !Thread.currentThread().isInterrupted()) {
// PingResponse is custom object
// Note:
// executePingRequest uses PingRequest to create a command
// which than create a runtime process to execute ping command
// using string response i am creating PingResponse
PingResponse pingResponse = PingUtils.executePingRequest(request);
if (pingResponse != null) {
pingResponse.setHopLocation(hopLocation);
// publish ping response to main GUI/handler
publishProgress(pingResponse);
} else
Logger.error(
"PingThread", "PingResponse isNull for " + request.toString()
);
}
}
}
现在如果我在一个循环中创建多个线程说超过 500 个并在池执行器中执行
执行线程
PingThread thread = new PingThread(params);
poolExecutor.execute(thread);
我知道LinkedBlockingQueue 在执行任务之前会保留它们。每个线程的处理时间最长为 200 到 400ms,但一般小于 10ms
我在做什么
for (int iteration = 1; iteration <= 50/*configurable*/; iteration++) {
for (int index = 0; index < 10/*configurable*/; index++) {
PingThread thread = new PingThread(someParams);
poolExecutor.execute(thread);
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Logger.error(false, e);
}
}
50 次迭代大约需要 25 秒,这里我最多只有 40 个 ping 响应,其余的被认为是由于超时而丢失。如果我增加迭代,损失也会增加(由于线程数的增加而呈指数增长)
观察:
我在 Galaxy S6 上运行这个应用程序,它有 8 个内核,应用程序池大小为 16,最大池大小为 16 + 2,我知道处理器一次只运行一个线程,它共享一个并行的量子时间处理。
通过及时观察ThreadPoolExecutor,我看到队列中有很多任务,超时后由于LinkedBlockingQueue,队列中仍然存在很多线程
如果我减少线程数它工作正常,但如果增加它会产生问题
问题:
- 当我使用具有双核处理器的设备时,Ping 响应会降低。
- 为什么队列中有很多线程,每个线程占用 大约 10 到 50 毫秒(增加线程时间会增加 uptp 300 毫秒或 更多)?
- 它应该在给定的时间内完成,为什么不呢?
- 如何解决这个问题?
- 我应该使用
ConcurrentLinkedQueue,但它使用生产者/消费者模型,不知何故ThreadPoolExecutor(我认为是)也使用此模型。 -
LinkedBlockingQueue在任务执行之前持有任务(线程空闲或在队列中),如何克服这个问题? - 为后面的迭代设置
Thread.MAX_PRIORITY并不能解决问题(后面的迭代的线程在队列中) - 减少线程数可以解决问题,为什么?因为队列中没有线程少?
- 有什么方法可以检查,如果队列中存在的线程招待他们,然后在不阻塞其他线程但在给定时间内执行其他线程。
- 增加 5 秒这样的额外时间不是解决方案
- 像 How to get the ThreadPoolExecutor to increase threads to max before queueing? 一样更改
corePoolSize在我的情况下不起作用。
在测试期间,内存和处理器的使用受到限制。
需要详细的答案/帮助。
编辑
当应用程序进入后台时,没有任何损失,用户 CPU 使用率下降到 0-2%,而焦点应用程序占用了 4-6% 的 cpu 使用率。是不是因为 UI 和其他乱七八糟的东西,我试图删除所有不必要的代码,我也确实将 PingThread 更改为 PingTask
PingTask implements Runnable {/*....*/}
注意: 我使用相同的代码创建了单独的基于 java 的应用程序,它在桌面上运行良好,所以我们可以说这是 android OS 特定的问题吗?
【问题讨论】:
-
你不需要为每个子进程运行一个线程,所以我建议你从那里开始。此外,您的应用程序的核心功能是 ping 和路由跟踪,您应该考虑在进程中实现和执行这些任务。如果您异步执行此操作,则不需要线程或子进程,并且可以更好地控制取消。所有这些都将显着提高您的应用程序在规格较低的设备(CPU 速度、内存、网络带宽等)上的性能、可扩展性和电池使用率
-
它的客户端要求在单独的线程中ping每个ip?你所说的过程中是什么意思?控制或取消不是这里的问题,因为我有有限的窗口来执行所有请求的 ping 并解析他们的响应,如果我这样做,损失将成倍增加
-
请将您调用的方法的代码发布到
PingThread。尤其是PingUtils.executePingRequest(request)方法 -
您是否尝试过同时运行多个 AsyncTask?喜欢:
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); -
@Stanojkovic
AsyncTask的THREAD_POOL_EXECUTOR与 Threads 或 Runnable 相比具有权重,而默认AsyncTask使用new LinkedBlockingQueue<Runnable>(128),如果为AsyncTask创建自定义池执行器,则不值得。当我们需要主动更新我们的 GUI 时,我们使用AsyncTask
标签: java android multithreading threadpoolexecutor blockingqueue