Выполняет ли 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
    });
}

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

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

еще раз, я очень извиняюсь за путаницу.

7 ответов


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

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

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

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

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


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

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.


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

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


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

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


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

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


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

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

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