2013-03-26 65 views
0

我試圖掃描我的Android設備中的所有文件。我用了一個多線程類是這樣的:多線程意外停止在Asynctask

public class FileScanner { 

// subfolders to explore 
private final Queue<File> exploreList = new ConcurrentLinkedQueue<File>(); 

private long fileCounter = 0; 

List<File> listFile = new ArrayList<File>(); 

public void count() { 
    fileCounter++; 
} 

public long getCounter() { 
    return this.fileCounter; 
} 

public List<File> getListFile() { 
    return this.listFile; 
} 

int[] threads; 

public FileScanner(int numberOfThreads) { 
    threads = new int[numberOfThreads]; 
    for (int i = 0; i < threads.length; i++) { 
     threads[i] = -1; 
    } 
} 

void scan(File file) { 

    // add the first one to the list 
    exploreList.add(file); 

    for (int i = 0; i < threads.length; i++) { 
     FileExplorer explorer = new FileExplorer(i, this); 
     Thread t = new Thread(explorer); 
     t.start(); 
    } 

    Thread waitToFinish = new Thread(new Runnable() { 

     @Override 
     public void run() { 

      boolean working = true; 
      while (working) { 
       working = false; 

       for (int i = 0; i < threads.length; i++) { 
        if (threads[i] == -1) { 
         working = true; 
         break; 
        } 
       } 

       try { 
        Thread.sleep(1); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 

     } 
    }); 

    waitToFinish.start(); 
} 

public void done(int id, int counter) { 
    threads[id] = counter; 
} 

public boolean isFinished() { 
    for (int i = 0; i < threads.length; i++) { 
     if (threads[i] == -1) { 
      return false; 
     } 
    } 
    return true; 
} 

class FileExplorer implements Runnable { 

    public int counter = 0; 
    public FileScanner owner; 
    private int id; 

    public FileExplorer(int id, FileScanner owner) { 
     this.id = id; 
     this.owner = owner; 
    } 

    @Override 
    public void run() { 
     while (!owner.exploreList.isEmpty()) { 

      // get the first from the list 
      try { 
       File file = (File) owner.exploreList.remove(); 

       if (file.exists()) { 

        if (!file.isDirectory()) { 
         count(); 
         listFile.add(file); 
        } else { 

         // add the files to the queue 
         File[] arr = file.listFiles(); 
         if (arr != null) { 
          for (int i = 0; i < arr.length; i++) { 
           owner.exploreList.add(arr[i]); 
          } 
         } 
        } 
       } 
      } catch (Exception e) { 
       e.printStackTrace(); 
       // silent kill :) 
      } 

      try { 
       Thread.sleep(1); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 

     owner.done(id, counter); 
    } 

} 

而且我把它在我的AsyncTask:

私有類FetchResidualAsynctask擴展的AsyncTask { FileScanner fileMachine;

@Override 
    protected void onPreExecute() { 
     super.onPreExecute(); 
     listResidualFileTemp.clear(); 
     listResidualFileThumbnail.clear(); 
     listResidualAppAds.clear(); 
     listResidualAppLeftOvers.clear(); 
     findAllStorage(); 
     for (int i = 0; i < listStorage.size(); i++) { 
      fileMachine = new FileScanner(20); 
      fileMachine.scan(listStorage.get(i)); 
      listFile.addAll(fileMachine.getListFile()); 
     } 
    } 

    @Override 
    protected Void doInBackground(Void... params) { 
    numberOfFiles = listFile.size(); 
     Log.i("numberOfFiles", "NUmber: " + numberOfFiles); 
     processindex = 0; 
     getActivity().runOnUiThread(new Runnable() { 
      public void run() { 
       mBtnClean.setText(R.string.btn_rescan); 
       mBtnClean.setEnabled(false); 
       txtResidualFile.setText(""); 
       mProgressbar.setVisibility(View.VISIBLE); 
       mProgressbar.setProgress(0); 
       mBtnClean.setText(R.string.btn_stop); 
       mBtnClean.setEnabled(true); 
       mProgressbar.setMax(numberOfFiles); 
      } 
     }); 

     for (int i = 0; i < listFile.size(); i++) { 
      getFilePath(listFile.get(i)); 
     } 

    } 

問題是文件列表返回如此混亂。在我調試時,每次測試結果都不一樣。第一次只返回少量文件(例如:160),下次更大(1200)。

我認爲FileScanner fileMachine.scan()尚未完成,強制停止運行到DoInBackground。

有人能幫助我解決這個問題嗎?

+0

我希望在DoInBackground開始運行之前掃描所有文件:) – Hanamichi 2013-03-26 20:40:27

回答

0

好問題。一般來說,不可能發射一堆線索,並以某種方式讓他們「工作」。相反,您需要創建一個預定義大小的線程池,並在需要完成的工作時將新線程打包。在某個時候,你想在一個線程上運行的任務將會等待,因爲沒有剩下線程。這是預期的行爲。爲了促進多線程的使用,決定預先想要的最大線程數,建立一個線程池,然後纔開始工作。培訓班Sending Operations to Multiple Threads對此進行了詳細描述。

+0

謝謝各位回覆。我發現問題是因爲我調用了listFile.addAll(fileMachine.getListFile());在功能完成之前。做一下檢查它的狀態,並在所有線程完成時添加。 – Hanamichi 2013-03-27 15:05:59

1

這看起來過於複雜和充​​滿競爭條件。你的主要bug可能是線程在它實際上是空的之前檢測到隊列是空的(然後線程退出)......也就是說,在某一時刻隊列已經暫時變空了(線程remove()d last項目),但然後一個線程添加了一些東西。

要等待您的工作人員完成......您可以使用Thread.join()或Semaphore,而不是使用那些複雜的不安全輪詢。

你甚至確定有這樣的並行化的好處嗎?我想20個線程都試圖同時敲擊文件系統,但實際上並不能享受到很多同時執行的工作。它甚至可能是文件系統驅動程序序列化所有IO請求!