ios 在 Grand Central Dispatch 中使用 dispatch_sync
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4607125/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
using dispatch_sync in Grand Central Dispatch
提问by Rasputin Jones
Can anyone explain with really clear use cases what the purpose of dispatch_sync
in GCD
is for? I can't understand where and why I would have to use this.
谁能用非常清晰的用例来解释dispatch_sync
in的目的GCD
是什么?我无法理解在哪里以及为什么必须使用它。
Thanks!
谢谢!
回答by David Gelhar
You use it when you want to execute a block and wait for the results.
当您想执行一个块并等待结果时,您可以使用它。
One example of this is the pattern where you're using a dispatch queue instead of locks for synchronization. For example, assume you have a shared NSMutableArray a
, with access mediated by dispatch queue q
. A background thread might be appending to the array (async), while your foreground thread is pulling the first item off (synchronously):
这方面的一个示例是您使用调度队列而不是锁进行同步的模式。例如,假设您有一个共享的 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
}
});
回答by user4951
First understand its brother dispatch_async
先了解一下它的兄弟 dispatch_async
//Do something
dispatch_async(queue, ^{
//Do something else
});
//Do More Stuff
You use dispatch_async
to create a new thread. When you do that, the current thread will not stop. That means //Do More Stuff
may be executed before //Do something else
finish
你dispatch_async
用来创建一个新线程。当你这样做时,当前线程不会停止。这意味着//Do More Stuff
可能会在//Do something else
完成之前执行
What happens if you want the current thread to stop?
如果您希望当前线程停止会发生什么?
You do not use dispatch at all. Just write the code normally
你根本不使用 dispatch 。正常写代码就行
//Do something
//Do something else
//Do More Stuff
Now, say you want to do something on a DIFFERENTthread and yet wait as if and ensure that stuffs are done consecutively.
现在,假设您想在不同的线程上做某事,但又要等待并确保连续完成。
There are many reason to do this. UI update, for example, is done on main thread.
这样做的原因有很多。例如,UI 更新是在主线程上完成的。
That's where you use dispatch_sync
这就是你使用的地方 dispatch_sync
//Do something
dispatch_sync(queue, ^{
//Do something else
});
//Do More Stuff
Here you got //Do something
//Do something else
and //Do More stuff
done consecutively even though //Do something else
is done on a different thread.
在这里//Do something
//Do something else
,//Do More stuff
即使//Do something else
是在不同的线程上完成,您也可以连续完成。
Usually, when people use different thread, the whole purpose is so that something can get executed without waiting. Say you want to download large amount of data but you want to keep the UI smooth.
通常,当人们使用不同的线程时,整个目的是让某些事情无需等待即可执行。假设您想下载大量数据,但又想保持 UI 流畅。
Hence, dispatch_sync is rarely used. But it's there. I personally never used that. Why not ask for some sample code or project that does use dispatch_sync.
因此,很少使用 dispatch_sync。但它就在那里。我个人从未使用过。为什么不要求一些使用 dispatch_sync 的示例代码或项目。
回答by Catfish_Man
dispatch_sync is semantically equivalent to a traditional mutex lock.
dispatch_sync 在语义上等同于传统的互斥锁。
dispatch_sync(queue, ^{
//access shared resource
});
works the same as
工作原理相同
pthread_mutex_lock(&lock);
//access shared resource
pthread_mutex_unlock(&lock);
回答by Krzysztof Przygoda
David Gelharleft unsaid that his example will work only because he quietly created serial queue (passed NULL in dispatch_queue_create what is equal to DISPATCH_QUEUE_SERIAL).
David Gelhar没有说他的例子会起作用,只是因为他悄悄地创建了串行队列(在 dispatch_queue_create 中传递了 NULL 等于 DISPATCH_QUEUE_SERIAL)。
If you wish create concurrent queue (to gain all of multithread power), his code will lead to crash because of NSArray mutation (addObject:) during mutation (removeObjectAtIndex:) or even bad access (NSArray range beyond bounds). In that case we should use barrier to ensure exclusive access to the NSArray while the both blocks run. Not only does it exclude all other writes to the NSArray while it runs, but it also excludes all other reads, making the modification safe.
如果您希望创建并发队列(以获得所有多线程能力),他的代码将导致由于 NSArray 突变 (addObject:) 在突变 (removeObjectAtIndex:) 甚至错误访问(NSArray 范围超出边界)期间崩溃。在这种情况下,我们应该使用屏障来确保在两个块运行时对 NSArray 的独占访问。它不仅在运行时排除了对 NSArray 的所有其他写入,而且还排除了所有其他读取,从而使修改安全。
Example for concurrent queue should look like this:
并发队列的示例应如下所示:
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.
回答by user4951
If you want some samples of practical use look at this question of mine:
如果您想要一些实际使用的示例,请查看我的这个问题:
How do I resolve this deadlock that happen ocassionally?
I solve it by ensuring that my main managedObjectContext is created on the main thread. The process is very fast and I do not mind waiting. Not waiting means I will have to deal with a lot of concurency issue.
我通过确保在主线程上创建我的主 managedObjectContext 来解决它。这个过程非常快,我不介意等待。没有等待意味着我必须处理大量的并发性问题。
I need dispatch_sync because some code need to be done on main thread, which is the different thread than the one where to code is being executed.
我需要 dispatch_sync 因为一些代码需要在主线程上完成,主线程与正在执行代码的线程不同。
So basically if you want the code to 1. Proceed like usual. You don't want to worry about race conditions. You want to ensure that the code is completed before moving on. 2. Done on a different thread
所以基本上如果你想要代码为 1。像往常一样继续。你不想担心竞争条件。您希望在继续之前确保代码已完成。2. 在不同的线程上完成
use dispatch_sync.
使用 dispatch_sync。
If 1 is violated, use dispatch_async. If 2 is violated just write the code like usual.
如果违反 1,则使用 dispatch_async。如果违反了 2,只需像往常一样编写代码。
So far, I only do this once, namely when something need to be done on main thread.
到目前为止,我只做一次,即当需要在主线程上做一些事情时。
So here's the code:
所以这是代码:
+(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]];
}
回答by Hari Kunwar
dispatch_sync is mainly used inside dispatch_async block to perform some operations on main thread(like update ui).
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;
});
});
回答by nes1983
Here's a half-way realistic example. You have 2000 zip files that you want to analyze in parallel. But the zip library isn't thread-safe. Therefore, all work that touches the zip library goes into the unzipQueue
queue. (The example is in Ruby, but all calls map directly to the C library. "apply", for example, maps to dispatch_apply(3))
这是一个半现实的例子。您有 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
回答by Aardvark
I've used dispatch sync when inside an async dispatch to signal UI changes back to the main thread.
我在异步调度中使用调度同步将 UI 更改发回主线程。
My async block holds back only a little and I know the main thread is aware of the UI changes and will action them. Generally used this in a processing block of code that takes some CPU time but I still want to action UI changes from within that block. Actioning the UI changes in the async block is useless as UI, I believe, runs on the main thread. Also actioning them as secondary async blocks, or a self delegate, results in the UI only seeing them a few seconds later and it looks tardy.
我的异步块只保留了一点,我知道主线程知道 UI 更改并将对其进行操作。通常在需要一些 CPU 时间的代码处理块中使用它,但我仍然希望从该块内操作 UI 更改。在异步块中操作 UI 更改是无用的,因为我相信 UI 在主线程上运行。还将它们作为辅助异步块或自委托进行操作,会导致 UI 仅在几秒钟后看到它们,并且看起来很慢。
Example block:
示例块:
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 */});
}
});