任何人都可以是很清楚的用例解释的目的dispatch_sync
在GCD
是什么?我不明白在哪里以及为什么必须使用它。
谢谢!
Answers:
当您要执行块并等待结果时,可以使用它。
这样的一个例子是您使用调度队列而不是使用锁进行同步的模式。例如,假设您有一个共享的NSMutableArray a
,访问权限由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
}
});
-performSelector:onThread:withObject:waitUntilDone:
或performSelectorOnMainThread:withObject:waitUntilDone:
设置waitUntilDone
为YES相似。
首先了解它的兄弟 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
是在另一个异步过程中用作回调。例如,Core Data的NSManagedObjectContextperformBlock
方法可以在块末尾将其用作回调。
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范围超出范围)而导致崩溃。在那种情况下,我们应该使用barrier来确保在两个块都运行时以独占方式访问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]];
}
这是一个现实的例子。您有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 */});
}
});
dispatch_async
后跟一个并没有多大价值dispatch_sync
。但是,当您想在另一个队列上生成多个并发作业,然后等待所有这些作业时,此相同模式很有用。