任何人都可以用非常明确的用例来解释dispatch_sync
中GCD
的目的是什么?我无法理解我必须使用它的地点和原因。
谢谢!
答案 0 :(得分:77)
首先了解其兄弟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的示例代码或项目。
答案 1 :(得分:76)
当您想要执行一个块并等待结果时,可以使用它。
这方面的一个示例是您使用调度队列而不是锁来进行同步的模式。例如,假设您有一个共享的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
}
});
答案 2 :(得分:25)
答案 3 :(得分:4)
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.
答案 4 :(得分:3)
如果你想要一些实际使用的样品,请看看我的这个问题:
How do I resolve this deadlock that happen ocassionally?
我通过确保在主线程上创建主要的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]];
}
答案 5 :(得分:2)
dispatch_sync主要用于dispatch_async块内部,以在主线程上执行某些操作(如update 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;
});
});
答案 6 :(得分:0)
这是一个中间的现实例子。您有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
答案 7 :(得分:-1)
我在异步调度内部使用了调度同步,以便将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 */});
}
});