2

iOS アプリケーションのバックグラウンドで多くのことを実行したいが、スレッドを作成して (たとえば GCD を使用して) このバックグラウンド アクティビティを実行するように適切にコーディングしたとします。

ある時点で変数の更新を書き込む必要があるが、この更新が発生する可能性がある場合、または作成したスレッドのいずれかが発生する場合はどうでしょう。

明らかにその変数を保護したいので、キーワード@synchronizedを使用してロックを作成できますが、ここに問題があります (Apple のドキュメントからの抜粋)。

ディレクティブは@synchronized()、単一のスレッドで使用するためにコードのセクションをロックします。スレッドが保護されたコードを終了するまで、つまり、実行がブロック内の最後のステートメントを超えて継続するまで、他のスレッドはブロックされます@synchronized()

つまり、オブジェクトを同期し、2 つのスレッドが同時にそれを書き込んでいる場合、両方のスレッドがデータの書き込みを完了するまで、メイン スレッドでさえブロックされます。

これらすべてを紹介するコードの例:

// Create the background queue
dispatch_queue_t queue = dispatch_queue_create("synchronized_example", NULL);

// Start working in new thread
dispatch_async(queue, ^
               {                   
                   // Synchronized that shared resource
                   @synchronized(sharedResource_)
                   {   
                       // Write things on that resource
                       // If more that one thread access this piece of code:
                       // all threads (even main thread) will block until task is completed.
                       [self writeComplexDataOnLocalFile];
                   }                         
               });

// won’t actually go away until queue is empty
dispatch_release(queue);

したがって、質問はかなり単純です。これを克服するにはどうすればよいですか? その場合、ブロックする必要がないことがわかっているメインスレッドを除くすべてのスレッドにロックを安全に追加するにはどうすればよいでしょうか?

明確化のために編集

何人かがコメントしたように、ロックを取得しようとしている 2 つのスレッドだけが、両方が完了するまでブロックする必要があるというのは論理的に思えます (そして、これは明らかに同期を使用するときに最初に考えたことでした)。

ただし、実際の状況でテストしたところ、そうではないようで、メイン スレッドもロックに悩まされているようです。

UI がブロックされないように、このメカニズムを使用して別のスレッドにログを記録します。しかし、集中的にログを記録すると、UI (メイン スレッド) は明らかに大きな影響を受けます (スクロールはそれほどスムーズではありません)。

したがって、ここで 2 つのオプションがあります。バックグラウンド タスクが重すぎてメイン スレッドにさえ影響を与えるか (これは疑問です)、ロック操作の実行中に同期もメイン スレッドをブロックします (これについては再検討を始めています)。

Time Profiler を使用して、もう少し掘り下げます。

4

2 に答える 2

3

Appleのドキュメントから引用した次の文を誤解していると思います。

他のスレッドは、スレッドが保護されたコードを終了するまでブロックされます。

これは、すべてのスレッドがブロックされていることを意味するのではなく、同じオブジェクト(この例では)で同期しようとしているすべてのスレッドがブロックされていることを意味し_sharedResourceます。

次の引用は、Appleのスレッドプログラミングガイドから引用したものです。これにより、同じオブジェクトで同期するスレッドのみがブロックされることが明確になります。

@synchronizedディレクティブに渡されるオブジェクトは、保護されたブロックを区別するために使用される一意の識別子です。上記のメソッドを2つの異なるスレッドで実行し、各スレッドのanObjパラメーターに異なるオブジェクトを渡すと、それぞれがロックを取得し、他のスレッドによってブロックされることなく処理を続行します。ただし、どちらの場合も同じオブジェクトを渡すと、一方のスレッドが最初にロックを取得し、もう一方のスレッドは最初のスレッドがクリティカルセクションを完了するまでブロックします。

更新:バックグラウンドスレッドがインターフェイスのパフォーマンスに影響を与えている場合は、バックグラウンドスレッドにスリープを設定することを検討してください。これにより、メインスレッドがUIを更新できるようになります。

GCDを使用していることは承知していますがNSThread、たとえば、スレッドを一時停止するメソッドがいくつかあります-sleepForTimeInterval:。GCDでは、おそらく単に呼び出すことができますsleep()

または、スレッドの優先度を低い優先度に変更することも検討してください。繰り返しNSThreadますがsetThreadPriority:、この目的のためにあります。GCDでは、ディスパッチされたブロックに優先度の低いキューを使用するだけだと思います。

于 2012-04-24T13:33:38.320 に答える
3

私があなたを正しく理解しているかどうかはわかりませんが、@synchronizeすべてのスレッドをブロックするのではなく、ブロック内でコードを実行したいスレッドのみをブロックします。したがって、解決策はおそらく次のとおりです。メインスレッドでコードを実行しないでください。

メインスレッドがロックを取得することを単に避けたい場合は、これを行うことができます(そして大混乱を引き起こします):

dispatch_async(queue, ^
               {              
                   if(![NSThread isMainThread])
                   {
                       // Synchronized that shared resource
                       @synchronized(sharedResource_)
                       {   
                           // Write things on that resource
                           // If more that one thread access this piece of code:
                           // all threads (even main thread) will block until task is completed.
                           [self writeComplexDataOnLocalFile];
                       }          
                   }
                   else
                       [self writeComplexDataOnLocalFile];               
               });
于 2012-04-24T13:29:21.193 に答える