Как я могу запустить метод после выполнения двух фоновых задач в iOS? [Дубликат]

Использование Node.js

Если система имеет , возможно использовать -p print и -e флаги сценария evaulate с JSON.parse , чтобы вытащить любое требуемое значение.

Простой пример с использованием строки JSON { "foo": "bar" } и вытаскивание значения «foo»:

$ node -pe 'JSON.parse(process.argv[1]).foo' '{ "foo": "bar" }'
bar

] Поскольку у нас есть доступ к cat и другим утилитам, мы можем использовать это для файлов:

$ node -pe 'JSON.parse(process.argv[1]).foo' "$(cat foobar.json)"
bar

Или любой другой формат, такой как URL-адрес, содержащий JSON:

$ node -pe 'JSON.parse(process.argv[1]).name' "$(curl -s https://api.github.com/users/trevorsenior)"
Trevor Senior

173
задан Rob 26 January 2013 в 21:31
поделиться

8 ответов

Использовать группы отправки: см. здесь для примера «Ожидание групп заданий в очереди» в главе «Очереди отправки» в Руководстве по программированию параллельной работы библиотеки разработчиков iOS для разработчиков iOS

Ваш пример может выглядеть примерно так:

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block1
    NSLog(@"Block1");
    [NSThread sleepForTimeInterval:5.0];
    NSLog(@"Block1 End");
});


dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block2
    NSLog(@"Block2");
    [NSThread sleepForTimeInterval:8.0];
    NSLog(@"Block2 End");
});

dispatch_group_notify(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block3
    NSLog(@"Block3");
});

// only for non-ARC projects, handled automatically in ARC-enabled projects.
dispatch_release(group);

и может выдавать такой вывод:

2012-08-11 16:10:18.049 Dispatch[11858:1e03] Block1
2012-08-11 16:10:18.052 Dispatch[11858:1d03] Block2
2012-08-11 16:10:23.051 Dispatch[11858:1e03] Block1 End
2012-08-11 16:10:26.053 Dispatch[11858:1d03] Block2 End
2012-08-11 16:10:26.054 Dispatch[11858:1d03] Block3
278
ответ дан Umar Farooque 21 August 2018 в 11:14
поделиться
  • 1
    Круто. будут ли асинхронные задачи / блоки, связанные с группой, выполняться последовательно или одновременно? Я имею в виду, предположим, что block1 и block2 теперь связаны с группой, будет ли блок2 ждать, пока блок1 не будет выполнен, прежде чем он сможет начать выполнение? – tom 10 August 2012 в 23:25
  • 2
    Это зависит от вас. dispatch_group_async аналогичен dispatch_async с добавленным параметром группы. Поэтому, если вы используете разные очереди для block1 и block2 или планируете их в одной и той же параллельной очереди, они могут запускаться одновременно; если вы планируете их в одной и той же последовательной очереди, они будут запускаться серийно. Это ничем не отличается от планирования блоков без групп. – Jörn Eyrich 11 August 2012 в 15:12
  • 3
    Это также относится к выполнению сообщения в веб-службе? – Little Tiny Dev 17 April 2014 в 10:36
  • 4
    Вы заметили, что время не равно времени сна, установленному в вашем блоке? почему это будет так? – Damon Yuan 2 August 2015 в 12:56
  • 5
    В ARC просто удалите dispatch_release (group); – loretoparisi 28 September 2015 в 12:19

Ответы выше, все круто, но все они пропустили одно. group выполняет задачи (блоки) в потоке, в который он был введен, когда вы используете dispatch_group_enter / dispatch_group_leave.

- (IBAction)buttonAction:(id)sender {
      dispatch_queue_t demoQueue = dispatch_queue_create("com.demo.group", DISPATCH_QUEUE_CONCURRENT);
      dispatch_async(demoQueue, ^{
        dispatch_group_t demoGroup = dispatch_group_create();
        for(int i = 0; i < 10; i++) {
          dispatch_group_enter(demoGroup);
          [self testMethod:i
                     block:^{
                       dispatch_group_leave(demoGroup);
                     }];
        }

        dispatch_group_notify(demoGroup, dispatch_get_main_queue(), ^{
          NSLog(@"All group tasks are done!");
        });
      });
    }

    - (void)testMethod:(NSInteger)index block:(void(^)(void))completeBlock {
      NSLog(@"Group task started...%ld", index);
      NSLog(@"Current thread is %@ thread", [NSThread isMainThread] ? @"main" : @"not main");
      [NSThread sleepForTimeInterval:1.f];

      if(completeBlock) {
        completeBlock();
      }
    }

выполняется в созданной параллельной очереди demoQueue. Если я не создаю какую-либо очередь, она запускается в основном потоке.

- (IBAction)buttonAction:(id)sender {
    dispatch_group_t demoGroup = dispatch_group_create();
    for(int i = 0; i < 10; i++) {
      dispatch_group_enter(demoGroup);
      [self testMethod:i
                 block:^{
                   dispatch_group_leave(demoGroup);
                 }];
    }

    dispatch_group_notify(demoGroup, dispatch_get_main_queue(), ^{
      NSLog(@"All group tasks are done!");
    });
    }

    - (void)testMethod:(NSInteger)index block:(void(^)(void))completeBlock {
      NSLog(@"Group task started...%ld", index);
      NSLog(@"Current thread is %@ thread", [NSThread isMainThread] ? @"main" : @"not main");
      [NSThread sleepForTimeInterval:1.f];

      if(completeBlock) {
        completeBlock();
      }
    }

, и есть третий способ сделать задачи исполняемыми в другом потоке:

- (IBAction)buttonAction:(id)sender {
      dispatch_queue_t demoQueue = dispatch_queue_create("com.demo.group", DISPATCH_QUEUE_CONCURRENT);
      //  dispatch_async(demoQueue, ^{
      __weak ViewController* weakSelf = self;
      dispatch_group_t demoGroup = dispatch_group_create();
      for(int i = 0; i < 10; i++) {
        dispatch_group_enter(demoGroup);
        dispatch_async(demoQueue, ^{
          [weakSelf testMethod:i
                         block:^{
                           dispatch_group_leave(demoGroup);
                         }];
        });
      }

      dispatch_group_notify(demoGroup, dispatch_get_main_queue(), ^{
        NSLog(@"All group tasks are done!");
      });
      //  });
    }

Конечно, поскольку Вы можете использовать dispatch_group_async, чтобы получить то, что хотите.

2
ответ дан Bruce Lee 21 August 2018 в 11:14
поделиться

Не сказать, что другие ответы не подходят для определенных обстоятельств, но это один фрагмент, который я всегда пользовался Google:

- (void)runSigninThenInvokeSelector:(SEL)signInDoneSel {


    if (signInDoneSel) {
        [self performSelector:signInDoneSel];
    }

}
-1
ответ дан ChuckKelly 21 August 2018 в 11:14
поделиться

Расширение на Jörn Eyrich отвечает (повышайте его ответ, если вы продвигаете этот вариант), если у вас нет контроля над вызовами dispatch_async для ваших блоков, как это может быть в случае блоков завершения асинхронизации, вы можете использовать GCD группы, использующие dispatch_group_enter и dispatch_group_leave.

В этом примере мы притворяемся, что computeInBackground - это то, что мы не можем изменить (представьте, что это обратный вызов делегата, NSURLConnection completeHandler или что-то еще) и таким образом, у нас нет доступа к вызовам отправки.

// create a group
dispatch_group_t group = dispatch_group_create();

// pair a dispatch_group_enter for each dispatch_group_leave
dispatch_group_enter(group);     // pair 1 enter
[self computeInBackground:1 completion:^{
    NSLog(@"1 done");
    dispatch_group_leave(group); // pair 1 leave
}];

// again... (and again...)
dispatch_group_enter(group);     // pair 2 enter
[self computeInBackground:2 completion:^{
    NSLog(@"2 done");
    dispatch_group_leave(group); // pair 2 leave
}];

// Next, setup the code to execute after all the paired enter/leave calls.
//
// Option 1: Get a notification on a block that will be scheduled on the specified queue:
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    NSLog(@"finally!");
});

// Option 2: Block an wait for the calls to complete in code already running
// (as cbartel points out, be careful with running this on the main/UI queue!):
//
// dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // blocks current thread
// NSLog(@"finally!");

В этом примере computeInBackground: завершение: реализовано как:

- (void)computeInBackground:(int)no completion:(void (^)(void))block {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        NSLog(@"%d starting", no);
        sleep(no*2);
        block();
    });
}

Выход (с отметками времени от run):

12:57:02.574  2 starting
12:57:02.574  1 starting
12:57:04.590  1 done
12:57:06.590  2 done
12:57:06.591  finally!
254
ответ дан Ferenc Kiss 21 August 2018 в 11:14
поделиться
  • 1
    @ ɲeuroburɳ Вышеупомянутый код ждет основного потока. Я считаю, что это заблокирует основной поток и приведет к тому, что пользовательский интерфейс будет не отвечать на запросы до тех пор, пока вся группа не будет завершена. Я рекомендую переместить ожидание в фоновый поток. Например, dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_HIGH, 0) – cbartel 20 October 2014 в 17:41
  • 2
    @cbartel, хорошая добыча! Я обновил код примера, чтобы отразить ваш комментарий. Много раз вам нужно, чтобы обратный вызов находился в главной очереди - в этом случае, хотя dispatch_queue_notify, вероятно, лучше (если время блокировки не будет коротким). – ɲeuroburɳ 20 October 2014 в 21:34
  • 3
    Где я могу освободить группу (т. Е. Dispatch_release (group))? Я не уверен, что можно освободить в dispatch_group_notify. Но так как это код, который запускается после завершения группы, я не уверен, где выпустить. – GingerBreadMane 27 April 2015 в 21:42
  • 4
    Если вы используете ARC, вам не нужно вызывать dispatch_release: stackoverflow.com/questions/8618632/… – ɲeuroburɳ 29 April 2015 в 18:36
  • 5
    Хорошая статья, которая далее объясняет, что: commandshift.co.uk/blog/2014/03/19/… – Rizon 31 August 2015 в 21:40

С Swift 3, Grand Central Dispatch предлагает множество способов решить вашу проблему. В соответствии с вашими потребностями вы можете выбрать один из шести шаблонов, показанных в следующих фрагментах игровой площадки.


# 1. Используя методы DispatchGroup , DispatchGroup notify(qos:flags:queue:execute:) и DispatchQueue async(group:qos:flags:execute:)

Руководство по программированию параллельного программирования Apple Developer указывает на DispatchGroup :

Группы отправки - это способ блокировать поток, пока не завершится выполнение одной или нескольких задач. Вы можете использовать это поведение в тех местах, где вы не можете добиться прогресса, пока все указанные задачи не будут завершены. Например, после отправки нескольких задач для вычисления некоторых данных вы можете использовать группу для ожидания этих задач, а затем обрабатывать результаты, когда они будут выполнены.

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)
let group = DispatchGroup()

queue.async(group: group) {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
}

queue.async(group: group) {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
}

group.notify(queue: queue) {
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

# 2. Используя DispatchGroup , DispatchGroup wait() , DispatchGroup enter() и DispatchGroup leave()
import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)
let group = DispatchGroup()

group.enter()
queue.async {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
    group.leave()
}

group.enter()
queue.async {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
    group.leave()
}

queue.async {
    group.wait()
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

Обратите внимание, что вы также можете смешать DispatchGroup wait() с DispatchQueue async(group:qos:flags:execute:) или микс DispatchGroup enter() и DispatchGroup leave() с DispatchGroup notify(qos:flags:queue:execute:).


# 3. Использование свойства Dispatch​Work​Item​Flags barrier и DispatchQueue DispatchQueue async(group:qos:flags:execute:)

Учебное пособие по центральному диспетчерскому центру для Swift 3: Part 1/2 статья Raywenderlich.com дает определение барьеров :

Барьеры отправки - это группа функций, выполняющих узкие места в последовательном стиле при работе с параллельными очередями. [...] Когда вы отправляете DispatchWorkItem в очередь отправки, вы можете установить флаги, чтобы указать, что он должен быть единственным элементом, выполняемым в указанной очереди за это конкретное время. Это означает, что все элементы, отправленные в очередь до барьера отправки, должны завершиться до выполнения DispatchWorkItem.

Использование:

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)

queue.async {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
}

queue.async {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
}

queue.async(flags: .barrier) {
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

# 4. Используя DispatchWorkItem , свойство Dispatch​Work​Item​Flags barrier и DispatchQueue async(execute:)

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)

queue.async {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
}

queue.async {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
}

let dispatchWorkItem = DispatchWorkItem(qos: .default, flags: .barrier) {
    print("#3 finished")
}

queue.async(execute: dispatchWorkItem)

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

# 5. Используя DispatchSemaphore , DispatchSemaphore wait() и DispatchSemaphore signal() методы

Соруш Ханлоу написал следующие строки в Сообщение в блоге GCD Handbook :

Используя семафор, мы можем заблокировать поток в течение произвольного промежутка времени, пока сигнал из другого потока не будет послал. Семафоры, как и остальные GCD, являются потокобезопасными, и их можно запускать из любого места. Семафоры могут использоваться, когда есть асинхронный API, который вам нужно сделать синхронным, но вы не можете его изменить.

Справочник API разработчика Apple также дает следующее обсуждение для DispatchSemaphore init(value:​) initializer:

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

Использование:

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)
let semaphore = DispatchSemaphore(value: 0)

queue.async {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
    semaphore.signal()
}

queue.async {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
    semaphore.signal()
}

queue.async {
    semaphore.wait()
    semaphore.wait()    
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

# 6. Используя OperationQueue и BlockOperation

Справочник API разработчика Apple содержит информацию о Operation​Queue:

Операционные очереди используют библиотеку libdispatch (также известную как Grand Central Dispatch), чтобы инициировать выполнение своих операций.

Использование:

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let operationQueue = OperationQueue()

let blockOne = BlockOperation {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
}

let blockTwo = BlockOperation {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
}

let blockThree = BlockOperation {
    print("#3 finished")
}

blockThree.addDependency(blockOne)
blockThree.addDependency(blockTwo)

operationQueue.addOperations([blockThree, blockTwo, blockOne], waitUntilFinished: false)

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 or
 #2 started
 #1 started
 #2 finished
 #1 finished
 #3 finished
 */

41
ответ дан Imanou Petit 21 August 2018 в 11:14
поделиться

Первый ответ по существу правильный, но если вам нужен самый простой способ добиться желаемого результата, вот пример отдельного кода, демонстрирующий, как это сделать с помощью семафора (так же, как группы отправки работают за кулисами , JFYI):

#include <dispatch/dispatch.h>
#include <stdio.h>

main()
{
        dispatch_queue_t myQ = dispatch_queue_create("my.conQ", DISPATCH_QUEUE_CONCURRENT);
        dispatch_semaphore_t mySem = dispatch_semaphore_create(0);

        dispatch_async(myQ, ^{ printf("Hi I'm block one!\n"); sleep(2); dispatch_semaphore_signal(mySem);});
        dispatch_async(myQ, ^{ printf("Hi I'm block two!\n"); sleep(4); dispatch_semaphore_signal(mySem);});
        dispatch_async(myQ, ^{ dispatch_semaphore_wait(mySem, DISPATCH_TIME_FOREVER); printf("Hi, I'm the final block!\n"); });
        dispatch_main();
}
3
ответ дан jkh 21 August 2018 в 11:14
поделиться
  • 1
    Два наблюдения: 1. Вам не хватает dispatch_semaphore_wait. У вас есть два сигнала, поэтому вам нужно два ожидания. Как и ваше «завершение» блок запускается, как только первый блок сигнализирует семафор, но до того, как закончится другой блок; 2. Учитывая, что это вопрос iOS, я бы отговорил использовать dispatch_main. – Rob 25 October 2013 в 04:40
  • 2
    Я согласен с Робом. Это недействительное решение. dispatch_semaphore_wait будет разблокирован, как только будет вызван любой из методов dispatch_semaphore_signal. Причина, по которой это может работать, заключается в том, что printf для блоков «один» и «два» происходит немедленно, а printf для «finally» происходит после ожидания - таким образом, после того, как блок спал за 2 секунд. Если вы поместите printf после вызовов sleep, вы получите результат для «один», а затем через 2 секунды для «finally», а затем через 2 секунды для «two». – ɲeuroburɳ 3 January 2014 в 19:48

Я знаю, что вы спросили о GCD, но если вы хотите, NSOperationQueue также обрабатывает этот материал действительно изящно, например :

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Starting 3");
}];

NSOperation *operation;

operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Starting 1");
    sleep(7);
    NSLog(@"Finishing 1");
}];

[completionOperation addDependency:operation];
[queue addOperation:operation];

operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Starting 2");
    sleep(5);
    NSLog(@"Finishing 2");
}];

[completionOperation addDependency:operation];
[queue addOperation:operation];

[queue addOperation:completionOperation];
55
ответ дан Rob 21 August 2018 в 11:14
поделиться
  • 1
    Это нормально, когда код внутри вашего NSBlockOperation является синхронным. Но что, если это не так, и вы хотите запустить завершение, когда ваша асинхронная операция завершена? – Greg Maletic 23 October 2013 в 19:10
  • 2
    @GregMaletic В этом случае я создаю подкласс NSOperation, который является параллельным и устанавливает isFinished, когда асинхронный процесс завершается. Тогда зависимости работают нормально. – Rob 23 October 2013 в 19:23
  • 3
  • 4
    @GregMaletic Да, вы также можете использовать это (пока dispatch_semaphore_wait не будет выполняться в основной очереди и пока ваши сигналы и ожидания будут сбалансированы). Пока вы не блокируете основную очередь, семафорный подход прекрасен, если вам не нужна гибкость операций (например, возможность отменить их, способность контролировать степень параллелизма и т. Д.). – Rob 25 October 2013 в 04:47
  • 5
    Есть ли проблемы, если я не использовал сон (4); – Himanth 8 June 2017 в 14:00
  • 6
    Нет, конечно, нет проблем с этим. На самом деле, вы практически никогда не хотите sleep()! Я только добавил те sleep() призывы к педагогическим причинам, чтобы блоки работали достаточно долго, чтобы вы могли видеть, что они работают одновременно. В этом тривиальном примере, в отсутствие sleep(), эти два блока могут выполняться так быстро, что отправленный блок может начинаться и заканчиваться, прежде чем у вас будет возможность эмпирически наблюдать параллельное выполнение. Но не делайте sleep() в своем коде. – Rob 8 June 2017 в 14:06
  • 7
    @ Reza.Ab - Если вам нужна задача, чтобы завершить задачу до начала двух задач, добавьте зависимость между этими задачами. Или, если очередь всегда выполняет только одну задачу за раз, сделайте ее последовательной очередью, установив maxConcurrentOperationCount на 1. Вы также можете установить приоритет операций как qualityOfService, так и queuePriority, но они имеют гораздо более тонкое влияние на приоритет задачи, чем зависимости и / или степень параллелизма в очереди. – Rob 17 February 2018 в 19:02
  • 8
    Мне нравится большинство ваших ответов, это исключение :( – J-Q 14 June 2018 в 15:16
  • 9
    @ J-Q - Спасибо, наверное. лол. Я согласен с ответами на отправку групп (и я их поддержал). Но, ИМХО, эти ответы неполны: любое обсуждение этой общей темы о том, чтобы отправить задание, ожидая завершения других, является неполным без признания барьеров. Барьеры - очень полезный способ выполнения некоторой задачи, ожидающей завершения других, и являются основой многих критических шаблонов (например, шаблона считывателя-писателя). При необходимости используйте группы, но не забывайте о барьерах! – Rob 14 June 2018 в 17:07
55
ответ дан Rob 1 November 2018 в 05:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: