74

谁能用非常清晰的用例解释dispatch_syncin的目的GCD是什么?我不明白我必须在哪里以及为什么要使用它。

谢谢!

4

8 回答 8

78

当您想要执行一个块并等待结果时使用它。

其中一个示例是您使用调度队列而不是锁进行同步的模式。例如,假设您有一个共享的 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
        }
});
于 2011-01-05T18:18:42.687 回答
78

先了解一下它的兄弟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 的示例代码或项目。

于 2012-10-05T04:15:53.987 回答
26

dispatch_sync 在语义上等同于传统的互斥锁。

dispatch_sync(queue, ^{
    //access shared resource
});

工作方式与

pthread_mutex_lock(&lock);
//access shared resource
pthread_mutex_unlock(&lock);
于 2011-01-06T17:25:21.773 回答
5

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.
于 2014-01-29T16:40:20.310 回答
3

如果您想要一些实际使用的示例,请查看我的这个问题:

如何解决偶尔发生的这种僵局?

我通过确保在主线程上创建我的主 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]];
}
于 2012-10-12T00:43:27.610 回答
2

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;
    });
});
于 2016-01-08T18:37:39.977 回答
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
于 2011-06-16T14:08:34.623 回答
-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 */});
         }

 });
于 2013-01-02T10:02:31.610 回答