我正在尝试扫描Android设备中的所有文件。我使用了这样的多线程类:
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 :(得分:1)
这看起来过于复杂,充满了竞争条件。您的主要错误可能是线程在实际为空之前检测到队列为空(然后线程退出)...即,在某个时刻队列变为暂时为空(线程删除()d最后一个item)但是一个线程会向它添加一些内容。
等待你的工作人员完成......你可以使用Thread.join()或Semaphore,而不是那种复杂的不安全轮询。
你是否确定并行化这样的东西有什么好处?我想20个线程都试图同时锤击文件系统实际上并没有享受很多同时执行。甚至可能是文件系统驱动程序序列化所有IO请求!
答案 1 :(得分:0)
好问题。一般来说,不可能触发一堆线程,并以某种方式使它们“工作”。相反,您需要创建一个预定义大小的线程池,并在您有工作要做时将一个新的线程包裹起来。在某些时候,您想要在线程上运行的任务将等待,因为没有线程留下。这是预期的行为。为了便于多线程使用,请事先确定所需的最大线程数,构建线程池,然后才开始执行工作。培训课程Sending Operations to Multiple Threads详细描述了这一点。