6

私は iPad アプリを開発していますが、現在、マルチスレッドへの最適なアプローチを見つけるのに苦労しています。簡単な例でこれを説明しましょう
。2 つのサブビュー、ディレクトリ ピッカー、および選択したディレクトリ内のすべての画像のサムネイルを含むギャラリーを含むビューがあります。これらのサムネイルの「ダウンロード」と生成にはかなりの時間がかかる可能性があるため、ビューの相互作用と更新がブロックされないようにマルチスレッドが必要です。

これは私がすでに試したことです:
[self performSelectorInBackground:@selector(displayThumbnails:) withObject:currentFolder];
ユーザーの操作がブロックされなかったため、これはうまくいきましたが、最初のフォルダーがまだロードされている間にユーザーが別のフォルダーをタップすると、悲惨なことに失敗します。2 つのスレッドが同じビューと変数にアクセスしようとしており、その結果、互いの適切な実行が台無しになります。ユーザーが別のフォルダをタップするdisplayThumbnailsと、現在ロードされているフォルダの が中止されます。これを行う方法が見つかりませんでした..

NSThreads
これを試してみましたが、最初の方法とほぼ同じ問題に苦労しました。進行中の方法をキャンセルする (簡単な) 方法が見つかりませんでした。(はい、知って[aThread cancel]いますが、スレッドを「再開」する方法が見つかりませんでした)。たぶんNSThread、独自の isRunning などのメソッドをサブクラス化して実装する必要がありますか? しかし、私が見落としているより良い方法や 3 番目 (または 4 番目と 5 番目) のオプションはありませんか?

これはかなり単純な例だと思いますNSThread。それで、あなたは何をしますか?あなたの意見をお願いします!

4

3 に答える 3

6

NSOperationQueueは、このタスクでうまく機能するはずです。

別のオプションは単純なGCDですが、それを使用したことがない場合は、「正しい方法」で物事を実装するようにほぼ自動的にガイドし、キャンセルの明白な方法があるため、 NSOperationQueue がおそらくより良い選択です。

于 2012-07-15T22:18:08.353 に答える
5

Concurrent NSOperations を使用して、バックグラウンドで画像をダウンロードして処理したいと考えています。これらは NSOperationsQueue によって管理されます。基本的に、これらの操作は、操作ごとに 1 つの画像を取得して処理し、ファイル システムに保存してから、画像が利用可能であることをメイン スレッドでメイン アプリに返すように構成されます。

github には、これを行う方法を示すプロジェクトがいくつかあります。「Concurrent」または「NSOperation」を使用して github を検索するだけです。

iOS には、バックグラウンド作業を行うための非常に優れた機能があります。グランド セントラル ディスパッチ (GCD) とブロックですが、デリゲート コールバックを使用してオブジェクトを作成することはできません。つまり、NSOperation です。

したがって、ブロックや GCD について調べてから、オープン ソースの Concurrent NSOperations コードを確認する必要があります。Concurrent NSOperations の使用は、ブロックを使用するほど単純ではありません。

于 2012-07-15T22:19:02.987 に答える
0

この問題が発生した場合は、おそらく次のようなアプローチを採用します。

  • 画像をロードし、メインスレッドに結果を表示させる単一のスレッド(私はGUIオブジェクトでスレッドを混乱させるのが好きではありません)

  • 新しいディレクトリが要求されたとき...まあ、それはあなたが物事をどのように管理したいかに依存します。基本的に、標準のキュー構造(条件変数と配列)をメインスレッドに使用して、パス名を渡すことで「このディレクトリが必要になる」ことをスレッドに通知できます。スレッドは、画像を読み込んでいるときでも(すべての画像の後など)キューをチェックし、新しいディレクトリが表示されるたびに切り替えます。

  • すべての状態を保持するディレクトリリーダーオブジェクトを作成し、パスによってインデックス付けされたこれをディクショナリに格納できます。新しいディレクトリが要求された場合は、最初にそのディクショナリを確認し、このディレクトリにオブジェクトがない場合にのみ新しいオブジェクトを作成してください。そうすれば、部分的にロードされたディレクトリは、再び必要になるまで残り、最初からロードする代わりにロードを続けることができます。

スレッドの擬似コード:

while (forever)
   new element = nil
   if we have an active directory loader
       tell directory loader to load one image
       if false then make directory loader inactive
       lock queue condition
       if queue has elements
          new element = retrieve LAST element (we aren't interested in the others)
          empty queue
          unlock with status "empty"
       else
          unlock queue
   else
       lock queue on condition "has elements"
       new element = retrieve last element
       empty queue
       unlock with status "empty"
   if new element != nil
       if directory loader for new path does not exist
          setup new directory loader for new path
          store in dictionary
          make it the "active" one
       else
          make the current one the "active"

ディレクトリローダーに関しては、次のようになります。

read one image:
   if there are still images to read:
       read, process and store one
       return true
   else
       performSelectorOnMainThread with an "update GUI" method and the image list as parameter
       return false;

これは簡単なスケッチです。スレッドにいくつかのコードの重複があり、私が書いた方法では、すべての画像を読んだときにGUIを更新するだけで、画像を読んだときに表示されるようにはなりません。現在の画像リストをコピーするか、必要に応じて同期を追加する必要があります。

于 2012-07-15T22:32:24.890 に答える