多线程在Asynctask中意外停止

时间:2013-03-26 20:36:41

标签: java android multithreading android-asynctask

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

有人可以帮我这个吗?

2 个答案:

答案 0 :(得分:1)

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

等待你的工作人员完成......你可以使用Thread.join()或Semaphore,而不是那种复杂的不安全轮询。

你是否确定并行化这样的东西有什么好处?我想20个线程都试图同时锤击文件系统实际上并没有享受很多同时执行。甚至可能是文件系统驱动程序序列化所有IO请求!

答案 1 :(得分:0)

好问题。一般来说,不可能触发一堆线程,并以某种方式使它们“工作”。相反,您需要创建一个预定义大小的线程池,并在您有工作要做时将一个新的线程包裹起来。在某些时候,您想要在线程上运行的任务将等待,因为没有线程留下。这是预期的行为。为了便于多线程使用,请事先确定所需的最大线程数,构建线程池,然后才开始执行工作。培训课程Sending Operations to Multiple Threads详细描述了这一点。