在Grand Central Dispatch中使用dispatch_sync
任何人都可以用真正清楚的用例来解释GCD
中dispatch_sync
的用途是什么? 我不明白在哪里,为什么我不得不使用这个。
谢谢!
当你想要执行一个块并等待结果时使用它。
其中一个例子就是您使用调度队列而不是同步锁的模式。 例如,假设你有一个共享的NSMutableArray a
,访问由调度队列q
调用。 后台线程可能会附加到数组(asynchronous),而您的前台线程closures(同步)第一个项目:
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
来创build一个新的线程。 当你这样做,当前线程不会停止。 这意味着/ / //Do More Stuff
可能会执行//Do something else
之前//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很less使用。 但它在那里。 我个人从来没有使用过。 为什么不要求使用dispatch_sync的示例代码或项目。
dispatch_sync在语义上等同于传统的互斥锁。
dispatch_sync(queue, ^{ //access shared resource });
和…一样
pthread_mutex_lock(&lock); //access shared resource pthread_mutex_unlock(&lock);
如果你想要一些实际使用的样品看看我的这个问题:
我如何解决这个偶然发生的僵局?
我通过确保在主线程上创build主要的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]]; }
大卫Gelhar留下没有说他的例子将工作,只是因为他悄悄地创build串行队列(dispatch_queue_create等于DISPATCH_QUEUE_SERIAL传递NULL)。
如果你想创build并发队列(获得所有的multithreading能力),他的代码将导致崩溃,因为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.
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”,例如maps到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
在asynchronous调度中,我使用了调度同步来将UI更改通知给主线程。
我的asynchronous块阻挡了一点,我知道主线程知道UI的变化,并会采取行动。 通常在需要一些CPU时间的处理代码块中使用这个,但是我仍然想要在该块内动作UI更改。 在asynchronous块中操作UI更改是无用的,因为UI相信在主线程上运行。 同时将它们作为辅助asynchronous块或自我委托来执行,导致用户界面仅在几秒钟之后才看到它们,并且看起来很迟缓。
示例块:
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 */}); } });