40

У меня есть сценарий в моем приложении, где я хочу выполнить некоторую трудоемкую задачу, которая состоит из некоторой обработки данных, а также обновления пользовательского интерфейса в методе. Мой метод выглядит так,Does 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), ^{ 

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

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

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

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


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

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

Еще раз, я действительно извиняюсь за замешательство.

+1

Возможно 'dispatch_sync' может быть полезным здесь ... –

+0

@DaveDeLong Возможно. Я вижу разработчиков, использующих 'dispatch_sync', потому что это кажется логичным, но на самом деле это часто не нужно. EmptyStack не поделился тем, почему он хочет подождать, так что нам нельзя советовать, хорошая ли это идея или нет. – Rob

+1

@Rob yep, 'dispatch_sync' может быть очень опасным, потому что намного легче зайти в тупик. Но * иногда * это правильная вещь. :) –

ответ

94

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

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

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

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

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

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

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

+0

Прохладный. Спасибо за объяснение. Вы имеете в виду, что он будет работать нормально, если я изменю все ** dispatch_async (dispatch_get_main_queue() ** на ** dispatch_sync (dispatch_get_main_queue() **? – EmptyStack

+3

Да, это сделает их синхронными (ждущими). ​​ –

+0

Отлично. – EmptyStack

1

Нет, это не будет ждать.

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

+0

Cool .. Спасибо .. – EmptyStack

+0

Но для таких вещей, как обновления пользовательского интерфейса, вы не _want_, чтобы подождать. Если вы хотите подождать, просто добавьте отправку в фоновый поток в качестве последнего инструкции кода, которые вы отправляете в основной поток. – gnasher729

+0

, который делает этот материал GCD. Таким образом, отправка отправки становится более чистой и лучшей. – Karsten

10

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

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 или ,

+0

Прохладный. Спасибо за Ваш ответ! – EmptyStack

8

Ваш предлагаемый 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 (где фоновый процесс будет не дождитесь обновления пользовательского интерфейса) или синхронно с dispatch_sync (где он будет ждать обновления пользовательского интерфейса), вопрос почему вы хотели бы сделать это синхронно: вы действительно хотите замедлить фоновый процесс, поскольку он ожидает обновления пользовательского интерфейса, или вы хотите, чтобы фоновый процесс выполнялся во время обновления пользовательского интерфейса.

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


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

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

+0

Упс. На самом деле мой исходный код похож на то, что вы разместили выше. Я пропустил, чтобы опубликовать это в моем вопросе. Сожалею. Я обновил свой вопрос. Спасибо за ваши БОЛЬШИЕ ПРЕДЛОЖЕНИЯ. – EmptyStack

0

ОК, есть два способа сделать это:

// GLOBAL_CONCURRENT_QUEUE 


- (void)doCalculationsAndUpdateUIsWith_GlobalQUEUE 
{ 
    dispatch_queue_t globalConcurrentQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
    dispatch_async(globalConcurrentQ, ^{ 

     // DATA PROCESSING 1 
     sleep(1); 
     NSLog(@"Hello world chekpoint 1"); 
     dispatch_sync(dispatch_get_main_queue(), ^{ 
      // UI UPDATION 1 
      sleep(1); 
      NSLog(@"Hello world chekpoint 2"); 
     }); 

     /* the control to come here after UI UPDATION 1 */ 
     sleep(1); 
     NSLog(@"Hello world chekpoint 3"); 
     // DATA PROCESSING 2 

     dispatch_sync(dispatch_get_main_queue(), ^{ 
      // UI UPDATION 2 
      sleep(1); 
      NSLog(@"Hello world chekpoint 4"); 
     }); 

     /* the control to come here after UI UPDATION 2 */ 
     sleep(1); 
     NSLog(@"Hello world chekpoint 5"); 
     // DATA PROCESSING 3 

     dispatch_sync(dispatch_get_main_queue(), ^{ 
      // UI UPDATION 3 
      sleep(1); 
      NSLog(@"Hello world chekpoint 6"); 
     }); 
    }); 
} 



// SERIAL QUEUE 
- (void)doCalculationsAndUpdateUIsWith_GlobalQUEUE 
{ 

    dispatch_queue_t serialQ = dispatch_queue_create("com.example.MyQueue", NULL); 
    dispatch_async(serialQ, ^{ 

     // DATA PROCESSING 1 
     sleep(1); 
     NSLog(@"Hello world chekpoint 1"); 

     dispatch_sync(dispatch_get_main_queue(), ^{ 
      // UI UPDATION 1 
      sleep(1); 
      NSLog(@"Hello world chekpoint 2"); 
     }); 


     sleep(1); 
     NSLog(@"Hello world chekpoint 3"); 
     // DATA PROCESSING 2 

     dispatch_sync(dispatch_get_main_queue(), ^{ 
      // UI UPDATION 2 
      sleep(1); 
      NSLog(@"Hello world chekpoint 4"); 
     }); 
    }); 
} 
+0

это не worl в фоновом режиме. Можете ли вы дать мне, что можете работать в фоновом режиме? –

+0

Я тебя не понял, просто удалите сон из основной темы. – kokemomuke

2

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

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 Элементы, отправленные в очередь, будут выполняться с приоритетом фона, то есть очередь будет запланирована для выполнения после того, как все очереди с более высоким приоритетом будут запланированы, и система будет запускать элементы в этой очереди в потоке с фоновым статусом в соответствии с заданием приоритета (2) (т.е. дисковый ввод-вывод дросселируется, а приоритет планирования потока установлен на минимальное значение).

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