dispatch_sync
inの目的が何であるかを本当に明確なユースケースで説明できる人GCD
はいますか? どこで、なぜこれを使用しなければならないのか理解できません。
ありがとう!
dispatch_sync
inの目的が何であるかを本当に明確なユースケースで説明できる人GCD
はいますか? どこで、なぜこれを使用しなければならないのか理解できません。
ありがとう!
ブロックを実行して結果を待ちたいときに使用します。
この一例は、同期のためにロックの代わりにディスパッチ キューを使用しているパターンです。たとえば、共有 NSMutableArraya
があり、アクセスが dispatch queue によって仲介されているとしますq
。フォアグラウンド スレッドが最初の項目を (同期的に) プルしている間に、バックグラウンド スレッドが配列に追加 (非同期) している可能性があります。
NSMutableArray *a = [[NSMutableArray alloc] init];
// All access to `a` is via this dispatch queue!
dispatch_queue_t q = dispatch_queue_create("com.foo.samplequeue", NULL);
dispatch_async(q, ^{ [a addObject:something]; }); // append to array, non-blocking
__block Something *first = nil; // "__block" to make results from block available
dispatch_sync(q, ^{ // note that these 3 statements...
if ([a count] > 0) { // ...are all executed together...
first = [a objectAtIndex:0]; // ...as part of a single block...
[a removeObjectAtIndex:0]; // ...to ensure consistent results
}
});
まず兄弟を理解するdispatch_async
//Do something
dispatch_async(queue, ^{
//Do something else
});
//Do More Stuff
を使用dispatch_async
して、新しいスレッドを作成します。これを行うと、現在のスレッドは停止しません。つまり、終了//Do More Stuff
前に実行される可能性があります//Do something else
現在のスレッドを停止したい場合はどうなりますか?
発送は一切ご利用いただきません。普通にコードを書くだけ
//Do something
//Do something else
//Do More Stuff
ここで、 DIFFERENTスレッドで何かを実行したいが、あたかも待っているかのように待機し、処理が連続して行われることを確認するとします。
これを行う理由はたくさんあります。たとえば、UI の更新はメイン スレッドで行われます。
そこで使うのはdispatch_sync
//Do something
dispatch_sync(queue, ^{
//Do something else
});
//Do More Stuff
ここでは、別のスレッドで実行されますが//Do something
//Do something else
、//Do More stuff
連続して実行されます。//Do something else
通常、人々が別のスレッドを使用する場合、全体的な目的は何かを待たずに実行できるようにすることです。大量のデータをダウンロードしたいが、UI をスムーズに保ちたいとします。
したがって、dispatch_sync はめったに使用されません。しかし、それはそこにあります。私は個人的にそれを使用したことはありません。dispatch_sync を使用するサンプル コードまたはプロジェクトを求めてみませんか。
dispatch_sync は、従来のミューテックス ロックと意味的に同等です。
dispatch_sync(queue, ^{
//access shared resource
});
と同じように動作します
pthread_mutex_lock(&lock);
//access shared resource
pthread_mutex_unlock(&lock);
David Gelharは、シリアル キューを静かに作成した (dispatch_queue_create で NULL を渡した (DISPATCH_QUEUE_SERIAL と同じ)) ためだけに、彼の例が機能するとは言いませんでした。
並行キューを作成したい場合 (マルチスレッドの能力をすべて得るために)、ミューテーション (removeObjectAtIndex:) 中の NSArray ミューテーション (addObject:) や不正なアクセス (NSArray 範囲が境界を超えている) のために、彼のコードはクラッシュにつながります。その場合、バリアを使用して、両方のブロックの実行中に NSArray への排他的アクセスを確保する必要があります。実行中に NSArray への他のすべての書き込みを除外するだけでなく、他のすべての読み取りも除外するため、変更が安全になります。
同時キューの例は次のようになります。
NSMutableArray *a = [[NSMutableArray alloc] init];
// All access to `a` is via this concurrent dispatch queue!
dispatch_queue_t q = dispatch_queue_create("com.foo.samplequeue", DISPATCH_QUEUE_CONCURRENT);
// append to array concurrently but safely and don't wait for block completion
dispatch_barrier_async(q, ^{ [a addObject:something]; });
__block Something *first = nil;
// pop 'Something first' from array concurrently and safely but wait for block completion...
dispatch_barrier_sync(q, ^{
if ([a count] > 0) {
first = [a objectAtIndex:0];
[a removeObjectAtIndex:0];
}
});
// ... then here you get your 'first = [a objectAtIndex:0];' due to synchronised dispatch.
// If you use async instead of sync here, then first will be nil.
実用的なサンプルが必要な場合は、私のこの質問を見てください。
時折発生するこのデッドロックを解決するにはどうすればよいですか?
メインの managedObjectContext がメイン スレッドで作成されるようにすることで解決します。プロセスは非常に高速で、待つことは気にしません。待機しないということは、多くの並行性の問題に対処しなければならないことを意味します。
コードを実行するスレッドとは異なるスレッドであるメインスレッドでコードを実行する必要があるため、dispatch_sync が必要です。
したがって、基本的にコードを 1 にしたい場合は、通常どおりに進みます。競合状態について心配する必要はありません。次に進む前に、コードが完成していることを確認する必要があります。2. 別のスレッドで実行
dispatch_sync を使用します。
1 に違反する場合は、dispatch_async を使用します。2に違反している場合は、通常どおりコードを記述してください。
これまでのところ、これを行うのは 1 回だけです。つまり、メイン スレッドで何かを実行する必要がある場合です。
コードは次のとおりです。
+(NSManagedObjectContext *)managedObjectContext {
NSThread *thread = [NSThread currentThread];
//BadgerNewAppDelegate *delegate = [BNUtilitiesQuick appDelegate];
//NSManagedObjectContext *moc = delegate.managedObjectContext;
if ([thread isMainThread]) {
//NSManagedObjectContext *moc = [self managedObjectContextMainThread];
return [self managedObjectContextMainThread];
}
else{
dispatch_sync(dispatch_get_main_queue(),^{
[self managedObjectContextMainThread];//Access it once to make sure it's there
});
}
// a key to cache the context for the given thread
NSMutableDictionary *managedObjectContexts =[self thread].managedObjectContexts;
@synchronized(self)
{
if ([managedObjectContexts objectForKey:[self threadKey]] == nil ) {
NSManagedObjectContext *threadContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
threadContext.parentContext = [self managedObjectContextMainThread];
//threadContext.persistentStoreCoordinator= [self persistentStoreCoordinator]; //moc.persistentStoreCoordinator;// [moc persistentStoreCoordinator];
threadContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
[managedObjectContexts setObject:threadContext forKey:[self threadKey]];
}
}
return [managedObjectContexts objectForKey:[self threadKey]];
}
dispatch_sync は主に、dispatch_async ブロック内で使用され、メイン スレッドでいくつかの操作を実行します (UI の更新など)。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Update UI in main thread
dispatch_sync(dispatch_get_main_queue(), ^{
self.view.backgroundColor = color;
});
});
これは中途半端な現実的な例です。並行して分析する 2000 個の zip ファイルがあります。ただし、zip ライブラリはスレッドセーフではありません。したがって、zip ライブラリに関係するすべての作業はunzipQueue
キューに入ります。(例は Ruby ですが、すべての呼び出しは C ライブラリに直接マップされます。たとえば、「適用」は、dispatch_apply(3)にマップされます)
#!/usr/bin/env macruby -w
require 'rubygems'
require 'zip/zipfilesystem'
@unzipQueue = Dispatch::Queue.new('ch.unibe.niko.unzipQueue')
def extractFile(n)
@unzipQueue.sync do
Zip::ZipFile.open("Quelltext.zip") { |zipfile|
sourceCode = zipfile.file.read("graph.php")
}
end
end
Dispatch::Queue.concurrent.apply(2000) do |i|
puts i if i % 200 == 0
extractFile(i)
end
非同期ディスパッチ内でディスパッチ同期を使用して、UIの変更をメインスレッドに戻しました。
私の非同期ブロックは少しだけ抑制し、メインスレッドがUIの変更を認識し、それらを実行することを知っています。通常、これはCPU時間を必要とするコードの処理ブロックで使用されますが、それでもそのブロック内からUIの変更を実行したいと思います。UIはメインスレッドで実行されるため、非同期ブロックでUIの変更を実行しても意味がありません。また、それらをセカンダリ非同期ブロックまたは自己委任としてアクションすると、UIは数秒後にのみそれらを認識し、遅延しているように見えます。
ブロックの例:
dispatch_queue_t myQueue = dispatch_queue_create("my.dispatch.q", 0);
dispatch_async(myQueue,
^{
// Do some nasty CPU intensive processing, load file whatever
if (somecondition in the nasty CPU processing stuff)
{
// Do stuff
dispatch_sync(dispatch_get_main_queue(),^{/* Do Stuff that affects UI Here */});
}
});