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

}

そして、私はそれを私の非同期タスクで呼び出します:

プライベートクラスFetchResidualAsynctaskはAsyncTaskを拡張します{FileScannerfileMachine;

    @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

良い質問。一般に、多数のスレッドを起動して、どういうわけかそれらを「機能」させることはできません。代わりに、事前定義されたサイズのスレッドのプールを作成し、作業が必要になったときに新しいスレッドを分割する必要があります。スレッドが残っていないため、ある時点で、スレッドで実行するタスクが待機します。これは予想される動作です。複数のスレッドを使用しやすくするために、必要なスレッドの最大数を事前に決定し、スレッドプールを構築してから、作業を開始してください。トレーニングクラスSendingOperationsto Multiple Threadsは、これについていくらか詳細に説明しています。

于 2013-03-26T22:12:17.230 に答える