在Grand Central Dispatch中使用dispatch_sync

时间:2022-05-03 09:01:22

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.

有谁可以用非常清晰的用例来解释在GCD中dispatch_sync的目的是什么?我不明白为什么我要用这个。

Thanks!

谢谢!

8 个解决方案

#1


76  

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,通过分派队列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


72  

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创建一个新的线程。当您这样做时,当前线程将不会停止。这意味着//做更多的事情可能会在//做其他事情之前完成

What happens if you want the current thread to stop?

如果希望当前线程停止,会发生什么?

You do not use dispatch at all. Just write the code normally

你根本不用分派。正常地编写代码即可

//Do something
//Do something else
//Do More Stuff

Now, say you want to do something on a DIFFERENT thread 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.

这里你得到//做某事/做某事/做某事/做更多连续完成的事情,即使//做某事是在另一个线程上完成的。

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的示例代码或项目呢?

#3


23  

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);

#4


3  

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]];
}

#5


3  

David Gelhar left 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.

如果您希望创建并发队列(以获得所有的多线程能力),那么他的代码将导致在突变(removeObjectAtIndex:)期间(甚至是糟糕的访问(NSArray范围超出界限)的NSArray突变(addObject:)导致崩溃。在这种情况下,我们应该使用barrier来确保对NSArray的独占访问,同时两个block都可以运行。它不仅排除了在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.

#6


3  

dispatch_sync is mainly used inside dispatch_async block to perform some operations on main thread(like update ui).

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;
    });
});

#7


0  

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

#8


-1  

I've used dispatch sync when inside an async dispatch to signal UI changes back to the main thread.

在异步分派中,我使用了dispatch sync,以将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.

我的async块只保留了一点,我知道主线程知道用户界面的变化,并将对它们进行操作。通常在需要一些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 */});
         }

 });

#1


76  

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,通过分派队列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


72  

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创建一个新的线程。当您这样做时,当前线程将不会停止。这意味着//做更多的事情可能会在//做其他事情之前完成

What happens if you want the current thread to stop?

如果希望当前线程停止,会发生什么?

You do not use dispatch at all. Just write the code normally

你根本不用分派。正常地编写代码即可

//Do something
//Do something else
//Do More Stuff

Now, say you want to do something on a DIFFERENT thread 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.

这里你得到//做某事/做某事/做某事/做更多连续完成的事情,即使//做某事是在另一个线程上完成的。

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的示例代码或项目呢?

#3


23  

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);

#4


3  

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]];
}

#5


3  

David Gelhar left 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.

如果您希望创建并发队列(以获得所有的多线程能力),那么他的代码将导致在突变(removeObjectAtIndex:)期间(甚至是糟糕的访问(NSArray范围超出界限)的NSArray突变(addObject:)导致崩溃。在这种情况下,我们应该使用barrier来确保对NSArray的独占访问,同时两个block都可以运行。它不仅排除了在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.

#6


3  

dispatch_sync is mainly used inside dispatch_async block to perform some operations on main thread(like update ui).

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;
    });
});

#7


0  

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

#8


-1  

I've used dispatch sync when inside an async dispatch to signal UI changes back to the main thread.

在异步分派中,我使用了dispatch sync,以将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.

我的async块只保留了一点,我知道主线程知道用户界面的变化,并将对它们进行操作。通常在需要一些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 */});
         }

 });