谁能用非常清晰的用例解释dispatch_sync
in的目的GCD
是什么?我不明白我必须在哪里以及为什么要使用它。
谢谢!
谁能用非常清晰的用例解释dispatch_sync
in的目的GCD
是什么?我不明白我必须在哪里以及为什么要使用它。
谢谢!
当您想要执行一个块并等待结果时使用它。
其中一个示例是您使用调度队列而不是锁进行同步的模式。例如,假设您有一个共享的 NSMutableArray a
,访问由调度队列介导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
现在,假设您想在不同的线程上做某事,但要等待并确保事情是连续完成的。
有很多理由这样做。例如,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)。
如果您希望创建并发队列(以获得所有多线程功能),他的代码将导致由于 NSArray 突变(addObject:) 在突变(removeObjectAtIndex:) 甚至错误访问(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,只需像往常一样编写代码。
到目前为止,我只做一次,即当需要在主线程上做某事时。
所以这里是代码:
+(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 库。“apply”,例如,映射到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 */});
}
});