2011-01-05 2 views

ответ

75

Вы используете его, когда хотите выполнить блок и дождаться результатов.

Одним из примеров этого является шаблон, в котором вы используете очередь отправки вместо блокировок для синхронизации. Например, предположим, что у вас есть общий 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 
     } 
}); 
+1

Я буду +1 это, так как это технически правильно, хотя я там не так много значения в этом один 'dispatch_async' с последующим' dispatch_sync' на одной и той же очереди. Однако этот же шаблон полезен, если вы хотите создать несколько параллельных заданий в другой очереди, а затем дождаться их всех. – kperryua

+0

Спасибо. Это начинает иметь смысл. Что делать, если я хочу запустить несколько параллельных потоков, используя dispatch_apply, которые обращаются к одному ресурсу с взаимным исключением. Как мне это сделать с GCD? Единственный способ использовать dispatch_async с последовательной очередью в моей dispatch_apply? Есть ли способ использовать dispatch_sync? –

+0

@kperryua - извините, если пример не ясен - идея в том, что отдельный поток будет делать несколько dispatch_async в очередь –

23

dispatch_sync семантический эквивалентно традиционный замок мьютекса.

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

работает так же, как

pthread_mutex_lock(&lock); 
//access shared resource 
pthread_mutex_unlock(&lock); 
+2

Это верно для последовательной очереди, но для параллельной очереди мы должны использовать dispatch_barrier_async для операции записи и dispatch_sync для операции чтения. –

0

Вот полпути реалистичный пример. У вас есть 2000 zip-файлов, которые вы хотите анализировать параллельно. Но zip-библиотека не является потокобезопасной. Поэтому вся работа, касающаяся zip-библиотеки, переходит в очередь unzipQueue. (В качестве примера в Ruby, но все вызовы отображаются непосредственно в библиотеку C. "Применить", например, карты для 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 
+9

Используйте псевдокод, если хотите что-то объяснить. Ruby, и др., Слишком специфичны и имеют высокий уровень. –

72

Сначала понять его брат 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 

Теперь предположим, что вы хотите сделать что-то на РАЗНЫЕ нить и все же ждать, как будто и убедитесь, что питания сделаны последовательно.

Есть много причин для этого. Обновление пользовательского интерфейса, например, выполняется в основном потоке.

Вот где вы используете dispatch_sync

//Do something 
dispatch_sync(queue, ^{ 
    //Do something else 
}); 
//Do More Stuff 

Здесь вы получили //Do something//Do something else и //Do More stuff сделано последовательно, даже если //Do something else делается на другом потоке.

Обычно, когда люди используют разные темы, вся цель состоит в том, чтобы что-то можно было выполнить без ожидания. Предположим, вы хотите загрузить большой объем данных, но хотите, чтобы пользовательский интерфейс был гладким.

Следовательно, dispatch_sync редко используется. Но он там. Я лично никогда не использовал это. Почему бы не попросить пример кода или проекта, который использует dispatch_sync.

+0

Это был отличный ответ для меня, спасибо. Пример использования 'dispatch_sync' происходит из другого асинхронного процесса для использования в качестве обратного вызова. Например, метод NMSanagedObjectContext «executeBlock» для Core Data может использовать его в конце блока в качестве обратного вызова. – guptron

+14

Как начинающий GCD, я нашел это предложение ложным: «Вы используете dispatch_async для создания нового потока». Из того, что я понял о GCD до сих пор, вызов dispatch_async не обязательно создает новый поток. Система будет обрабатывать создание потоков или атрибуцию каждой заданной очереди. –

+0

На самом деле я использую это много сейчас. Я могу выполнить код в фоновом потоке и dispatch_sync в основной поток. –

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

Я использовал отправку синхронизацию, когда внутри асинхронной отправки сигнализировать 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 */}); 
     } 

}); 
3

David Gelhar левые недосказанный, что его пример будет работать только потому, что он спокойно создали серийные очереди (передается NULL в dispatch_queue_create, что равно 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. 
3

dispatch_sync в основном используется внутри dispatch_async блока для выполнения некоторых операций на главном потоке (например, обновление пользовательского интерфейса).

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; 
    }); 
}); 
Смежные вопросы