2016-08-19 6 views
0

私はSAX(dd-plistライブラリ)で解析する大きなplist(Xml)ファイルを持っています。これは、解析のための大きなファイルであり、パフォーマンスの理由から私はマルチスレッドを使用する必要があります。私の目標は、plistファイルのキーの正確な数と同じスレッドの正確な数を持っています。キーとURLが等しい場合は、値を検索してURLと比較し、そうでない場合はnullを返し、スレッドをスキップしてキャンセルします(この値はHTMLコンテンツのタイトルであり、キーはplistに保存されているパス、およびurlは、Android WebViewのonPageFinishedでユーザーがクリックしてキャプチャしたリンクのURLです。もし私がコードを逃してしまったことを私が目標以上に教えてくれれば幸いです。 onPageFinishedの私WebFragment(android.support.v4.app.Fragment、では呼び出し可能ExecutiorService Javaを含むループを終了する方法(Android)

:ログここ

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; 
     } 
    } 
} 

されています:

ここ

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クラスです

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 
+0

同じファイル*を解析しようとするには複数のスレッドを使用していますか?... – Ordous

+0

@Ordousはい、最初に私はマルチスレッドをせずにメインスレッドで行いました。 SAX、だから私は多分、スレッドのそれぞれが同じplistファイルの1行を読む複数のスレッドを使用できると思った... – Jack

+0

OK、私はここであなたを落胆しようとはしていないが、かなり無駄。多くの場合、ボトルネックはファイルを読み込んでいるため、複数のスレッドでファイルを解析するのは馬鹿げた考えです。これはコア/スレッドの数ではなく、ここで問題となるアルゴリズムです。しかも、並行性がはっきりしていません。あなたの例では、実際には並行処理は行われません。単なるスレッドのハンドオフだけです。 "あなたのアプリが遅い場合は、単にマルチスレッド化してください!"実際にそれをプロファイリングし、問題がどこにあるのかを知ることができます。 – Ordous

答えて

2

接近に関する問題

非常に小さなタスクを他のスレッドに入れるという概念上の問題を除いて(実際のコンピューティングよりも多くの時間を費やす)、このコードには2つの主な問題があります:

1)実際のエラー。このエラーは、スタックメモリが不足しているために発生します。 Javaメモリは複数の領域に分割されており、最も広くよく知られているものの1つが「ヒープメモリ」です。これは、(ほぼ)すべてのオブジェクトが生きる場所です。あまり知られていない領域は「スタックメモリ」です。これはスレッドが現在の状態、スタックトレース、ローカル(メソッド)変数などを格納するためにメモリを取得する場所です。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; 
    } 
} 

あなたのアプローチとこの1つの大きな違いがあります。結果が見つかった場合、解析を続行しません。この特別なケースでを達成するには、まだ訪問していない先物のfuture.cancelを単に使用してにしてください。私はあなたにコードを残します。これは、スレッド間通信(スレッドを実行するスレッドを正常に停止するように通知する必要がありますが、これは簡単ではありません)が一般的に難しくなります。


アドバイスの言葉 - マルチスピードについて学び始めながら、スピードを上げようとするのは非常に生産的ではありません。周囲には多くの微妙な要素があります(上記のコードでは、2つの悪魔(ステートメントの並べ替えやメモリの可視性)について言及していません)。何かを並行して試してみるのがはるかに良いですが、必ずしも速くはありませんが、を右ににしてください。並列処理を正しく行うことができたら、それらをより速くすることができます。

関連する問題