0

我正在尝试扫描我的 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。

有人可以帮我解决这个问题吗?

4

2 回答 2

1

这看起来过于复杂并且充满了竞争条件。您的主要错误可能是线程在队列实际为空之前检测到队列为空(然后线程退出)......即在某个时刻队列暂时为空(最后一个线程 remove()d item) 但随后一个线程将某些内容添加回它。

要等待您的工作人员完成......您可以使用 Thread.join() 或信号量,而不是您已经获得的那种复杂的不安全轮询。

你确定并行这样的事情有好处吗?我想 20 个线程都试图同时敲击文件系统实际上并不能享受大量的同时执行。甚至可能是文件系统驱动程序序列化了所有 IO 请求!

于 2013-03-26T21:15:14.630 回答
0

好问题。一般来说,不可能触发一堆线程并以某种方式让它们“工作”。相反,您需要创建一个预定义大小的线程池,并在您有工作要做时分配一个新的线程池。在某些时候,您想在线程上运行的任务将等待,因为没有线程可用。这是预期的行为。为了方便多线程使用,请提前确定您想要的最大线程数,建立一个线程池,然后才开始工作。将操作发送到多个线程的培训课程对此进行了一些详细的描述。

于 2013-03-26T22:12:17.230 回答