gpt4 book ai didi

java - 如何退出包含Java的Callable ExecutiorService MultiThreading的循环(Android)

转载 作者:行者123 更新时间:2023-12-02 03:25:12 25 4
gpt4 key购买 nike

我有一个很大的plist(Xml)文件,可以用SAX(dd-plist库)解析。由于它是一个大文件,并且出于性能原因,我不得不使用multiThreading,我的目标是要拥有与我的plist文件中的键的确切数目相同的确切线程数,我的意思是对于plist中的每个键,单线程搜索该值并将其与url比较,如果key和url相等,则返回key的值,否则返回null并跳过并取消该线程(该值是html Content的标题,key为存储在plist中的路径,而url是链接用户在用户单击并在Android WebView的onPageFinished中捕获的任何url。如果有人告诉我实现上述目标,我将不胜感激。

在我的WebFragment(android.support.v4.app.Fragment,在onPageFinished中:

import com.dd.plist.NSDictionary;
import com.dd.plist.NSObject;
import com.dd.plist.PropertyListParser;

...
try {
is = getResources().openRawResource(R.raw.title);
rootDict = (NSDictionary) PropertyListParser.parse(is);
dict = new LinkedHashMap<>();
dict = rootDict.getHashMap();
ExecutorService executor = Executors.newFixedThreadPool(rootDict.size());
Future<String> future;
String myStr = null;
String key;
NSObject value;

for (Map.Entry<String, NSObject> entry : dict.entrySet()) {
key = entry.getKey();
value = entry.getValue();
// following line is refer to WebFragment (line 285 where logs complain and crash because of the memory
future = executor.submit(new ParsePlistThread(key, value, url.substring(32).toString()));
myStr = future.get();
if (myStr != null && !myStr.isEmpty()) {
break;
} else {
//future.cancel(true);
}
}
executor.shutdown();

if (myStr != null) {

if (numTab == 0) {
titleTextView.setText(myStr);
}
} catch (Exception ex) {
//Handle exceptions...
}


这是ParsePlistThread类:

import com.dd.plist.NSObject;

import java.util.concurrent.Callable;

/**
* Created by manager on 2016-08-18.
*/
public class ParsePlistThread implements Callable<String> {

public String key;
public NSObject valueObject;
public String url;
public ParsePlistThread(String key , NSObject valueObj , String url) {
this.key = key;
this.valueObject = valueObj;
this.url = url;
}

@Override
public String call() throws Exception {

if (key.equals(url)) {
return valueObject.toString();
} else

{
return null;
}
}
}


这是日志:

E/art: Throwing OutOfMemoryError "pthread_create (1040KB stack) failed: Try again"
08-19 09:52:50.328 28749-28749/ca.ccohs.oshanswers E/AndroidRuntime: FATAL EXCEPTION: main
Process: XXX, PID: 28749
java.lang.OutOfMemoryError: pthread_create (1040KB stack) failed: Try again
at java.lang.Thread.nativeCreate(Native Method)
at java.lang.Thread.start(Thread.java:1063)
at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:920)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1327)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:103)
at ca.ccohs.oshanswers.ui.WebFragment$3.onPageFinished(WebFragment.java:285)
at com.android.webview.chromium.WebViewContentsClientAdapter.onPageFinished(WebViewContentsClientAdapter.java:531)
at org.chromium.android_webview.AwContentsClientCallbackHelper$MyHandler.handleMessage(AwContentsClientCallbackHelper.java:188)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6117)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
08-19 09:52:50.343 2850-29945/? E/android.os.Debug: ro.product_ship = true
08-19 09:52:50.343 2850-29945/? E/android.os.Debug: ro.debug_level = 0x4f4c

最佳答案

您的方法存在问题

除了将极小的任务放入其他线程的概念性问题(与实际计算相比,您将花费更多的时间来传递信息)之外,此代码还有两个主要问题:

1)您得到的实际错误。此错误是由于堆栈内存不足而引起的。 Java内存分为多个区域,最大和最常见的已知区域之一是“堆内存”。这是(几乎)所有对象居住的地方。鲜为人知的区域是“堆栈内存”。这是线程在其中获取内存以存储当前状态,堆栈跟踪,局部(方法)变量等的位置。创建Thread时,将从该空间为其堆栈分配一些固定的内存。如果创建的线程过多,它将耗尽,并抛出诸如您遇到的错误之类的错误。

解决方案-重新使用线程!

执行程序具有内置功能,可以在完成任务时重用线程。在下面的更多内容。通常,CPU中的线程多于逻辑内核不会提高速度。

2)您实际上并没有在做任何事情。在循环中,您要向Executorexecutor.submit方法)提交任务,然后等待任务完成(future.get),然后转到下一行。因此,在创建新任务之前,您正在等待当前任务完成!您将不会有与此任务并行运行的2个任务。

最后一点是您不应该依赖多线程来加快文件处理。瓶颈几乎总是在读取文件。您很可能对它做一些愚蠢的事情,这会使它变慢。



多线程性能更好。

评论中提到,如果值得的话,看看如何解决这些错误可能很有用。下面是解决并发问题的半幼稚方法。

首先是第一-限制所需的线程。如果您在具有超线程的四核桌面上运行,我建议您使用6个线程,或者只是一个工作窃取池。我不确定什么是Android的好数字,但肯定比“非常大的文件”中的行数低。

ExecutorService executor = Executors.newFixedThreadPool(6);


要么

ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());


这样,当您的任务多于可用线程(因此,-数量多于可用处理器,除非您的线程受I / O绑定),而不是创建更多线程,新任务将排队等待,直到现有线程可用。

下一个问题实际上是将所有任务放到执行程序中以尽快执行,而不是顺序执行。为此,您必须跟踪自己创建的期货(注意,我还删除了每个循环中的子字符串,因为在两次调用之间URL似乎没有变化,因此可以对其进行预先计算。这是一个一般的事情-不要重做只能循环一次的循环!)

List<Future<String>> tasks = new ArrayList<>();
for (Map.Entry<String, NSObject> entry : dict.entrySet()) {
key = entry.getKey();
value = entry.getValue();
tasks.add(executor.submit(new ParsePlistThread(key, value, url)));
}


现在您已经提交了所有任务(我将再次重申,使用如此大量的小任务通常会适得其反),您需要收集结果。这样做很简单,只需遍历您的未来!

String result;
for (Future<String> fut : tasks) {
String taskResult = fut.get();
if (taskResult != null && !taskResult.isEmpty()) {
result = taskResult;
break;
}
}


您的方法与此方法之间有一个主要区别-如果发现结果,则不会继续进行解析。在这种特殊情况下,您只需在尚未访问的期货上使用 future.cancel即可实现。我将代码留给您。通常,这比较困难,因为这将涉及线程间通信(您必须发信号通知另一个线程以适当地停止其执行,这可能并不容易)。



一个建议:在尝试提高速度的同时开始学习多线程并不是很有效。它周围有很多微妙之处(上面甚至没有提到2个恶魔-语句重新排序和内存可见性),而正确,快速地完成它们是一个挑战!尝试并行执行某件事要好得多,但不一定要更快,但一定要正确。当您对正确进行并行处理感到满意时,可以考虑使它们更快。

关于java - 如何退出包含Java的Callable ExecutiorService MultiThreading的循环(Android),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39042419/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com