Objective-C-Wait for Loop with Block (NSURLSessionDataTasks) перед продолжением [duplicate]

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

Для двух исходных файлов, таких как:

  • inline111.cpp:
    #include 
    
    void bar();
    
    inline int fun() {
      return 111;
    }
    
    int main() {
      std::cout << "inline111: fun() = " << fun() << ", &fun = " << (void*) &fun;
      bar();
    }
    
  • inline222.cpp:
    #include 
    
    inline int fun() {
      return 222;
    }
    
    void bar() {
      std::cout << "inline222: fun() = " << fun() << ", &fun = " << (void*) &fun;
    }
    

  • Случай A: Скомпилировать :
    g++ -std=c++11 inline111.cpp inline222.cpp
    
    Выход :
    inline111: fun() = 111, &fun = 0x4029a0
    inline222: fun() = 111, &fun = 0x4029a0
    
    Обсуждение : Даже у вас должно быть одинаковое определение ваших встроенных функций, компилятор C ++ не указывает его, если это не (на самом деле, из-за отдельной компиляции у него нет способов проверить это). Это ваша обязанность обеспечить это! Linker не жалуется на Одно правило определения , поскольку fun() объявлено как inline. Однако, поскольку inline111.cpp является первой единицей перевода (которая на самом деле вызывает fun()), обработанной компилятором, компилятор создает экземпляр fun() на его первом вызове-вызове inline111.cpp. Если компилятор решает не расширять fun() по его вызову из любого места в вашей программе (, например из inline222.cpp), вызов fun() всегда будет связан с его экземпляром, созданным из inline111. cpp (вызов fun() внутри inline222.cpp также может привести экземпляр в этой единицы перевода, но он останется несвязанным). Действительно, это видно из идентичных распечаток &fun = 0x4029a0. Наконец, несмотря на предложение inline компилятору на самом деле расширить однострочный fun(), он полностью игнорирует ваше предложение, что ясно, потому что fun() = 111 в обе линии.

  • Случай B: Скомпилировать (уведомление об обратном порядке) :
    g++ -std=c++11 inline222.cpp inline111.cpp
    
    Выход :
    inline111: fun() = 222, &fun = 0x402980
    inline222: fun() = 222, &fun = 0x402980
    
    Обсуждение : этот случай утверждает то, что обсуждалось в случае А. Обратите внимание на важный момент, что если вы закомментируете фактический вызов fun() inline222.cpp ( ] например, полностью закомментировать cout -statement inline222.cpp), то, несмотря на порядок компиляции ваших единиц перевода, fun() будет создан при первом вызове вызова inline111.cpp, для случая B как inline111: fun() = 111, &fun = 0x402980.

  • Случай C: Компиляция (уведомление -O2) :
    g++ -std=c++11 -O2 inline222.cpp inline111.cpp
    
    или
    g++ -std=c++11 -O2 inline111.cpp inline222.cpp
    
    Выход :
    inline111: fun() = 111, &fun = 0x402900
    inline222: fun() = 222, &fun = 0x402900
    
    Обсуждение : Как описано здесь , оптимизация -O2 побуждает компилятор к фактически расширять функции, которые (обратите внимание также, что -fno-inline - по умолчанию без параметров оптимизации). Как видно из отпечатка здесь, fun() фактически был встроенным расширенным (согласно его определению в этой особой блоке трансляции), в результате чего два разных fun() распечатки. Несмотря на это, все еще существует только один глобально связанный экземпляр fun() (как требуется стандартом), как видно из идентичного &fun распечатки.

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

7 ответов

Использовать группы отправки: см. здесь для примера «Ожидание групп заданий в очереди» в главе «Очереди отправки» в Руководстве по программированию параллельной работы библиотеки разработчиков 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 24 August 2018 в 23:15
поделиться

Ответы выше, все круто, но все они пропустили одно. 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 24 August 2018 в 23:15
поделиться

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

- (void)runSigninThenInvokeSelector:(SEL)signInDoneSel {


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

}
-1
ответ дан ChuckKelly 24 August 2018 в 23:15
поделиться

Расширение на 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 24 August 2018 в 23:15
поделиться

С 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 24 August 2018 в 23:15
поделиться

Первый ответ по существу правильный, но если вам нужен самый простой способ добиться желаемого результата, вот пример отдельного кода, демонстрирующий, как это сделать с помощью семафора (так же, как группы отправки работают за кулисами , 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 24 August 2018 в 23:15
поделиться

Я знаю, что вы спросили о 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 24 August 2018 в 23:15
поделиться
Другие вопросы по тегам:

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