it-swarm.com.ru

Dispatch_async (dispatch_get_main_queue (), ^ {...}); подождать, пока не закончите?

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

- (void)doCalculationsAndUpdateUIs {

    // DATA PROCESSING 1
    // UI UPDATE 1

    // DATA PROCESSING 2
    // UI UPDATE 2

    // DATA PROCESSING 3
    // UI UPDATE 3
} 

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

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{

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

dispatch_async(dispatch_get_main_queue(), ^{

Наконец мой метод выглядит так,

- (void)doCalculationsAndUpdateUIs {

    // DATA PROCESSING 1 
    dispatch_async(dispatch_get_main_queue(), ^{
        // UI UPDATE 1
    });

    /* I expect the control to come here after UI UPDATE 1 */

    // DATA PROCESSING 2
    dispatch_async(dispatch_get_main_queue(), ^{
        // UI UPDATE 2
    });

    /* I expect the control to come here after UI UPDATE 2 */

    // DATA PROCESSING 3
    dispatch_async(dispatch_get_main_queue(), ^{
        // UI UPDATE 3
    });
}

Это действительно работает? Это действительно хорошая практика? Каков наилучший способ достичь этого?

Постскриптум Все эти три операции взаимосвязаны друг с другом.


Правка: Извините, ребята. Я пропустил строку в приведенном выше коде . Мой фактический код выглядит следующим образом.

- (void)doCalculationsAndUpdateUIs {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // DATA PROCESSING 1 
        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATE 1
        });

        /* I expect the control to come here after UI UPDATE 1 */

        // DATA PROCESSING 2
        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATE 2
        });

        /* I expect the control to come here after UI UPDATE 2 */

        // DATA PROCESSING 3
        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATE 3
        });
    });
}

Еще раз прошу прощения за путаницу.

41
EmptyStack

Нет, это не ждет, и то, как вы делаете это в этом примере, не является хорошей практикой.

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

Если вы хотите, чтобы обновление ожидало, вы можете использовать вместо него dispatch_sync.

// This will wait to finish
dispatch_sync(dispatch_get_main_queue(), ^{
    // Update the UI on the main thread.
});

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

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // Background work

    dispatch_async(dispatch_get_main_queue(), ^{
        // Update UI

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            // Background work

            dispatch_async(dispatch_get_main_queue(), ^{
                // Update UI
            });
        });
    });
});

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

101
David Rönnqvist

Вы должны поместить свою основную очередь диспетчеризации в блок, который выполняет вычисления. Например (здесь я создаю очередь отправки и не использую глобальную):

dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^{
  // Do some computation here.

  // Update UI after computation.
  dispatch_async(dispatch_get_main_queue(), ^{
    // Update the UI on the main thread.
  });
});

Конечно, если вы создаете очередь, не забудьте dispatch_release, если вы ориентируетесь на версию iOS до 6.0.

10
StatusReport

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

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

Таким образом это может выглядеть так:

- (void)doCalculationsAndUpdateUIs {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{

        // DATA PROCESSING 1 

        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATION 1
        });

        /* I expect the control to come here after UI UPDATION 1 */

        // DATA PROCESSING 2

        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATION 2
        });

        /* I expect the control to come here after UI UPDATION 2 */

        // DATA PROCESSING 3

        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATION 3
        });
    });
}

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

Обычно вы отправляете обновление пользовательского интерфейса асинхронно с помощью dispatch_async, как вы использовали в исходном вопросе. Да, безусловно, существуют особые обстоятельства, когда вам нужно синхронно отправлять код (например, вы синхронизируете обновления какого-либо свойства класса, выполняя все его обновления в главной очереди), но чаще всего вы просто отправляете обновление пользовательского интерфейса. асинхронно и продолжить. Синхронная диспетчеризация кода может вызвать проблемы (например, взаимные блокировки), если она выполняется небрежно, поэтому мой общий совет заключается в том, что вам, вероятно, следует отправлять обновления пользовательского интерфейса только синхронно, если есть настоятельная необходимость в этом, в противном случае вам следует разработать свое решение, чтобы вы могли отправлять их асинхронно ,.


Отвечая на ваш вопрос о том, является ли это «лучшим способом достижения этого», нам трудно сказать, не зная больше о решаемой бизнес-проблеме. Например, если вы могли бы вызывать эту doCalculationsAndUpdateUIs несколько раз, я мог бы склоняться использовать мою собственную последовательную очередь, а не параллельную глобальную очередь, чтобы гарантировать, что они не перешагнут друг через друга. Или, если вам может потребоваться возможность отменить эту doCalculationsAndUpdateUIs, когда пользователь закрывает сцену или снова вызывает метод, то я могу склониться к использованию очереди операций, которая предлагает возможности отмены. Это полностью зависит от того, чего вы пытаетесь достичь.

Но, в общем, шаблон асинхронной отправки сложной задачи в фоновую очередь и последующей асинхронной отправки обновления пользовательского интерфейса обратно в основную очередь очень распространен.

8
Rob

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

dispatch_queue_t globalConcurrentQueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

Это вернет параллельную очередь с заданным приоритетом, как указано в документации:

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

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

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

DISPATCH_QUEUE_PRIORITY_BACKGROUND Элементы, отправленные в очередь, будут работать с фоновым приоритетом, то есть очередь будет запланирована для выполнения после того, как все очереди с более высоким приоритетом были запланированы, и система будет запускать элементы в этой очереди в потоке с фоновым состоянием согласно setpriority (2) ( т.е. дисковый ввод-вывод регулируется, и приоритет планирования потока устанавливается на самое низкое значение). 

2
Ashokios
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^{
  // Do some computation here.

  // Update UI after computation.
  dispatch_async(dispatch_get_main_queue(), ^{
    // Update the UI on the main thread.
  });
});
1
Moin Shirazi

Нет, это не подождет.

Вы можете использовать performSelectorOnMainThread:withObject:waitUntilDone:.

1
Wain

Хорошей практикой является: Группы рассылки

dispatch_group_t imageGroup = dispatch_group_create();

dispatch_group_enter(imageGroup);
[uploadImage executeWithCompletion:^(NSURL *result, NSError* error){
    // Image successfully uploaded to S3
    dispatch_group_leave(imageGroup);
}];

dispatch_group_enter(imageGroup);
[setImage executeWithCompletion:^(NSURL *result, NSError* error){
    // Image url updated
    dispatch_group_leave(imageGroup);
}];

dispatch_group_notify(imageGroup,dispatch_get_main_queue(),^{
    // We get here when both tasks are completed
});
0
ChavirA