3
private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

上記のコード スニペットは、SerialExcutor を実装する AsyncTask ソース コードからのものですが、それがどのように機能するのか正確にはわかりません。

新しいタスクが到着すると、それは ArrayDeque の最後に配置され、ArrayDeque の一番上のタスクは、現在実行中の他のタスクがない場合にのみ実行されます。(mActive == null の場合)。

したがって、新しいタスクが到着したときにタスクが実行されている場合、何もトリガーされません。タスクの実行が終了すると、ArrayDeque は次のタスクを一番上にポップして実行しますか???

4

2 に答える 2

6

タスクはTHREAD_POOL_EXECUTORRunnable. このRunnableでは、実行中のタスクが何らかの理由で終了するscheduleNext()と、ブロック内で呼び出されfinallyます。キューにタスクがある場合、最初のタスクが実行されます。それ以外の場合、エグゼキュータは への次の呼び出しまでアイドル状態になりますexecute()。また、とをsynchronized別々のスレッドで同時に実行できないようにします。execute()scheduleNext()

于 2013-10-06T10:44:56.013 に答える
0

SerialExecutor クラスを詳しく見てみましょう。このクラスでは、最終的な

ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();

これは実際には、異なるスレッドでの異なるリクエストのシリアライザーとして機能します。これは、Half Sync Half Async パターンの例です。

次に、シリアル エグゼキュータがこれを行う方法を調べてみましょう。次のように記述されている SerialExecutor のコードの部分を見てください。

if (mActive == null) {
    scheduleNext();
}

そのため、Asynctask で execute が最初に呼び出されると、このコードがメイン スレッドで実行され (mActive が NULL に初期化されるため)、それによって scheduleNext() 関数が呼び出されます。ScheduleNext() 関数は次のように記述されています。

protected synchronized void scheduleNext() {
     if ((mActive = mTasks.poll()) != null) {
         THREAD_POOL_EXECUTOR.execute(mActive);
     }
 }

そのため、schedulenext() 関数では、デキューの最後に既に挿入した Runnable オブジェクトで mActive を初期化します。この Runnable オブジェクト (これは mActive に他なりません) は、スレッドプールから取得したスレッドで実行されます。そのスレッドで、「最後に」ブロックが実行されます。

現在、2 つのシナリオがあります。

  1. 別の AsyncTask インスタンスが作成され、最初のタスクが実行されているときに、そのインスタンスで execute メソッドを呼び出します。

  2. Execute メソッドは、最初のタスクが実行されるときに、AsyncTask の同じインスタンスで 2 回目に呼び出されます。

シナリオ I : のexecute関数を見るとSerialExecutor、バックグラウンド タスクを処理するための新しい実行可能なスレッド (スレッド t とします) を実際に作成していることがわかります。そのスレッド t 内で、 の run 関数を実行しますmActive。ただし、try ブロックにあるため、finally は、そのスレッドでバックグラウンド タスクが終了した後にのみ実行されます。(try と finally の両方が t のコンテキスト内で発生していることを思い出してください)。finally ブロック内で scheduleNext 関数を呼び出すと、mActive既にキューを空にしているため、 は NULL になります。ただし、同じものの別のインスタンスが作成され、それらに対して execute を呼び出すと、executeの前に同期キーワードが指定されているため、AsyncTaskこれらの execute 関数は実行されません。AsyncTaskSERIAL_EXECUTORは静的インスタンスです (したがって、同じクラスのすべてのオブジェクトは同じインスタンスを共有します... これはクラス レベルのロックの例です) つまり、同じ AsyncTask クラスのインスタンスは実行関数をプリエンプトできません (結果として、バックグラウンド タスクがスレッド t で実行されています)。つまり、タスクを実行しているアクティブなスレッドが 1 つしかないということです。このスレッドは、異なるタスクでは同じではないかもしれませんが、一度に 1 つのスレッドだけがタスクを実行します。したがって、最初のタスクが完了したときにのみ、後のタスクが次々に実行されます。これが と呼ばれる理由ですSerialExecutor

シナリオ II:この場合、例外エラーが発生します。execute 関数を同じ Asynctask オブジェクトで複数回呼び出すことができない理由を理解するには、以下のコード スニペットexecuteOnExecutorAsyncTask.java特に以下の部分を参照してください。

 if (mStatus != Status.PENDING) {
            switch (mStatus) {
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
                case FINISHED:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)");
            }
        }

上記のコード スニペットから明らかなように、タスクが実行中の状態にあるときに execute 関数を 2 回呼び出すと、「タスクを実行できません: タスクは既に実行されています。」という IllegalStateException がスローされます。

AsyncTask 内部に関する私の議論を読むことができます

https://docs.google.com/document/d/1_zihWXAwgTAdJc013-bOLUHPMrjeUBZnDuPkzMxEEj0/edit?usp=sharing

于 2013-11-26T09:15:07.847 に答える