В чем разница между синхронными и асинхронными вызовами в Objective-C и многопоточностью?
Долгое время я думал, что асинхронный - это синоним выполнения чего-либо в фоновом потоке, а синхронный - в основном потоке (блокирование обновлений и взаимодействий пользовательского интерфейса). Я понимаю, что не выполнение в основном потоке дорогостоящих действий объясняется тем, что оно не позволяет выполнять действия пользовательского интерфейса, поскольку основной поток занят, но почему синхронная работа является проблематичной?
Однако с тех пор как я обратил внимание, вы можете выполнять асинхронные вызовы в основном потоке и синхронные вызовы в фоновых потоках.
Я всегда слышу, как люди говорят, что не следует использовать дорогие вызовы синхронно или в главном потоке, так как это заблокирует пользовательский интерфейс. Эти две отдельные проблемы я должен убедиться, что я не делаю? Какие есть отличия?
8 ответов
Когда вы вызываете что-то синхронно, это означает, что поток, который инициировал эту операцию, будет ожидать завершения задачи, прежде чем продолжить. Асинхронный означает, что он не будет ждать.
Сказав это, когда люди предполагают, что вы выполняете какой-то медленный или дорогостоящий процесс асинхронно, они неявно предлагают не только выполнить асинхронно, но и делать это в фоновом потоке. Цель состоит в том, чтобы освободить основной поток, чтобы он мог продолжать отвечать на пользовательский интерфейс (а не зависать), чтобы асинхронно отправлять задачи в фоновый поток.
Итак, есть две части этого. Во-первых, используя GCD в качестве примера, вы получаете фоновую очередь (либо одну из глобальных фоновых очередей, либо создаете свою):
// one of the global concurrent background queues
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// or you could create your own serial background queue:
//
// dispatch_queue_t queue = dispatch_queue_create("com.domain.app.queuename", 0);
Во-вторых, вы отправляете свои задачи в эту очередь асинхронно:
dispatch_async(queue, ^{
// the slow stuff to be done in the background
});
Шаблон для очередей операций очень похож. Создайте очередь операций и добавьте операции в эту очередь.
В действительности, синхронное и асинхронное различие полностью отличается от основной очереди и фонового различия. Но когда люди говорят о "асинхронном запуске некоторого медленного процесса", они на самом деле говорят "асинхронно запускать некоторый медленный процесс в фоновой очереди".
"Синхронный" по сути означает "по порядку". По сути, когда вы выполняете синхронную операцию, все, что приходит позже, должно ждать завершения операции, прежде чем они начнутся.
И наоборот, "асинхронный" более или менее означает "не в порядке". Когда вы делаете что-то асинхронно, следующий код может быть немедленно запущен, и асинхронная операция будет выполнена... когда-нибудь. Он может выполняться параллельно с остальным кодом в другом потоке. Это может быть просто запланировано на какое-то другое время в том же потоке.
Понятие синхронности само по себе не имеет ничего общего с конкретными потоками. Речь идет о том, нужно ли вам ждать завершения операции или нет.
Основное внимание в этом уделяется программам Cocoa (Touch). AppKit запускает цикл основного события в главном потоке, поэтому, если основной поток ожидает завершения операции, он не может обработать ввод или обновить пользовательский интерфейс. Однако, если у вас есть фрагмент кода, работающий в фоновом потоке, выполнение синхронного кода не будет блокировать основной цикл событий, потому что это не основной поток, ожидающий завершения синхронной операции.
Точно так же длительная асинхронная операция из фонового потока, которую вы помещаете в основной поток, может вызвать проблемы, потому что, хотя фоновый поток не собирается ждать завершения операции, он все еще занимает время в основном потоке. где должен выполняться цикл событий.
Позвольте нам привести несколько простых примеров:
Асинхронный вызов с многопоточностью:
// Методы вызываются в другом потоке и не блокируют текущий поток. [NSURLConnection sendAsynchronousRequest: запрос Очередь: очередь completionHandler: ^(NSURLResponse * ответ, NSData * данные, NSError * ошибка) { }];
Синхронный вызов с многопоточностью:
//Сделай что-нибудь dispatch_sync(очередь, ^{ // Делай что-то еще }); // Делаем больше вещей
Здесь вы получаете // Делать что-то // Делать что-то еще и // Делать больше вещей, выполняемых последовательно, даже если // Делать что-то еще делается в другом потоке.
Обычно, когда люди используют разные потоки, цель состоит в том, чтобы что-то могло быть выполнено без ожидания. Скажем, вы хотите загрузить большой объем данных, но хотите, чтобы интерфейс был гладким.
Следовательно, dispatch_sync используется редко. Но это там. Я лично никогда не использовал это. Почему бы не попросить пример кода или проекта, который использует dispatch_sync.
Асинхронный вызов с одним потоком:
[self executeSelector:@selector(doSomething) withObject:nil afterDelay:0];
Здесь текущий цикл выполнения, который нужно завершить до вызова doSomething. Другими словами, текущий стек вызовов может быть завершен (текущий метод возвращается) до вызова doSomething.
Синхронный вызов с одним потоком:
[Сам сделай что-нибудь];
Я не думаю, что вам нужно объяснение этого.
В общем случае асинхронная активность не совпадает с многопоточностью, однако в iOS они реализованы с использованием этого способа. Это не верно для всех языков. Обычно мы управляем различными асинхронными задачами, используя циклы выполнения.
swift 3, 4, 4,2Синхронный означает, что поток, инициировавший эту операцию, будет ожидать завершения задачи, прежде чем продолжить.
DispatchQueue.main.sync {
}
Асинхронный означает, что завершение задачи в фоновом режиме и может уведомить вас о завершении означает, что он не будет ждать.
DispatchQueue.main.async {
}
Асинхронные средства вне строки, синхронные средства в строке. Вы можете выполнять синхронные задачи и блокировать несколько потоков одновременно.
Если вы находитесь в фоновом потоке и хотите обновить целый набор пользовательского интерфейса, который вы вызываете в главном потоке в dispatch
очередь. Если вы позвоните dispatch_sync
тогда код, в котором вы сейчас находитесь, ждет dispatch
завершить, тем самым блокируя фоновый поток, в котором вы находитесь, и блокируя пользовательский интерфейс, пока он обновляет основной поток.
Но если вы позвонили dispatch_async
фоновый поток продолжит работу с остальной частью кода, а основной поток запустит запрошенный dispatch
блок.
То же самое можно сказать, когда в основной теме. если вы позвоните dispatch_sync
из основного потока в глобальную или пользовательскую очередь он будет блокировать основной поток, пока он запускает код в отдельном потоке. Я не могу сказать, что знаю случай, когда это будет использовано, но это, безусловно, возможно.
Всякий раз, когда у вас есть код расчета, код веб-службы, выборка кода и все такое, что не влияет на пользовательский интерфейс, лучше всего делать это в отдельном потоке. Для такого рода вещей я бы сделал dispatch_async
в глобальном потоке. Затем, когда этот код будет завершен, я бы запустить dispatch_async
вернуться в основной поток, чтобы сказать ему, чтобы обновить пользовательский интерфейс с тем, что я только что рассчитал.
Синхронный означает блокирование, асинхронный означает, что он завершится в более позднее время (возможно, прямо сейчас), не блокируя то, что вы делаете в данный момент.
Это обсуждение в значительной степени отвечает на это: асинхронный против многопоточности - есть ли разница?
В общем случае асинхронный вызов не обязательно создает новый поток. Это один из способов его реализации, с другими способами уже существующий пул потоков или внешний процесс. Это сильно зависит от языка, объектной модели (если есть) и среды выполнения.
Асинхронный просто означает, что вызывающий поток не сидит и не ждет ответа, а также асинхронная активность не происходит в вызывающем потоке.
Поэтому в основном другие действия могут происходить в ожидании загрузки чего-либо, но это может выполняться или не выполняться в отдельных потоках.
Синхронизация против асинхронности
Синхронные и асинхронные операции касаются порядка выполнения следующей задачи по отношению к текущей задаче.
Давайте рассмотрим пример, где у нас есть три задачи (Задача 1, Задача 2, Задача 3) и мы будем оперировать Задачей 2. Задача — это атомарная операция — вызов метода в стеке (фрейме метода).
Синхронный
Подразумевает, что задачи будут выполняться одна за другой. Следующая задача запускается только после завершения текущей задачи. Задача 3 не запускается, пока не будет завершена задача 2.
Синхронизация + один поток = последовательный
main() {
task1()
task2()
task3()
}
![](/images/920d096d14c197b51940b7cddf5cba33f18bea64.png)
Синхронизация + многопоточность = параллельно
Заблокировано означает, что поток просто ожидает (хотя он может сделать что-то полезное, например, Java ExecutorService [About] и Future [About])
DispatchQueue.global().sync()
main() {
task1()
Future future = ExecutorService.submit(task2())
future.get() //<- blocked operation
task3()
}
![](/images/01c2b83e649afdb95619ba522702077e7a7abef7.png)
Асинхронный
Подразумевает, что задача немедленно возвращает управление с обещанием выполнить код и уведомить о результате позже (например, обратный вызов, функция). Задача 3 выполняется, даже если Задача 2 не завершена. асинхронный обратный вызов, обработчик завершения [О программе]
Асинхронный + один поток = параллельный
Используются Callback Queue (Message Queue) и Event Loop (RunLoop, Looper) [О программе] . Цикл обработки событий проверяет, пуст ли стек потоков, и если это так, он помещает первый элемент из очереди обратного вызова в стек потоков и снова повторяет эти шаги. Простыми примерами являются нажатие кнопки, публикация события...
Timer.scheduledTimer(withTimeInterval: 2, repeats: false)
main() {
task1()
ThreadMain.handler.post(task2());
task3()
}
![](/images/668635620a99605efdfc7568e45a1e3c90ab324f.png)
Асинхронный + многопоточный = одновременный и параллельный
Например, когда вам нужно сделать какие-то вычисления в другом потоке без блокировки. Вы можете использовать результат Задачи 2, используя метод блокировки get() или используя асинхронный обратный вызов через цикл.
DispatchQueue.global().async()
main() {
task1()
new Thread(task2()).start();
//or
Future future = ExecutorService.submit(task2())
task3()
}
![](/images/598c3e5ac755cac0c779106fc65b544ff2f294f2.png)
Примеры
Например, в мире мобильных устройств, где у нас есть пользовательский интерфейс/основной поток, и нам нужно что-то загрузить, у нас есть несколько вариантов:
- sync block — блокировать поток пользовательского интерфейса и ждать окончания загрузки. Пользовательский интерфейс не отвечает.
- асинхронный обратный вызов — создайте новый поток с асинхронным обратным вызовом для обновления пользовательского интерфейса (невозможно получить доступ к пользовательскому интерфейсу из потока, отличного от пользовательского интерфейса). Ад обратного звонка.
- async coroutine [О программе] — асинхронная задача с синхронным синтаксисом. Это позволяет смешивать задачу загрузки (функция приостановки) с задачей пользовательского интерфейса.
Синхронный: ожидает завершения задачи
executeBlockAndWait, синхронно. Основной поток остановится и будет ожидать ответа от блока, прежде чем он продолжит следующую операцию.
Асинхронный: завершает задачу в фоновом режиме и может уведомить вас о завершении.
executeBlock, метод, который является асинхронным. Это означает, что он может работать в любое время, и вызывающая его функция может продолжаться без необходимости знать, был ли блок успешным или нет. Основной поток может продолжаться, даже если блок еще не запущен или даже не запущен.