Ответ 1
Отбрасывая другой ответ, попробуйте объяснить, почему performBlockAndWait
всегда будет выполняться в вызывающем потоке.
performBlock
полностью асинхронен. Он всегда ставит очередь в очередь на очередь принимающего MOC, а затем немедленно возвращается. Таким образом,
[moc performBlock:^{
// Foo
}];
[moc performBlock:^{
// Bar
}];
разместит два блока в очереди для moc. Они будут выполняться асинхронно. Некоторая неизвестная нить вытащит блоки из очереди и выполнит их. Кроме того, эти блоки обернуты в свой собственный пул автозаполнения, а также они будут представлять полное событие пользователя Core Data (processPendingChanges
).
performBlockAndWait
НЕ использует внутреннюю очередь. Это синхронная операция, выполняемая в контексте вызывающего потока. Конечно, он будет ждать, пока текущие операции в очереди не будут выполнены, а затем этот блок будет выполняться в вызывающем потоке. Это документировано (и подтверждено в нескольких презентациях WWDC).
Кроме того, performBockAndWait
является повторным, так что вложенные вызовы происходят в этом вызывающем потоке.
Инженеры Core Data были очень понятны, что фактический поток, в котором выполняется операция MOC на основе очереди, не имеет значения. Это синхронизация с помощью ключа performBlock*
API.
Итак, рассмотрим "executeBlock" как "Этот блок помещается в очередь, который должен быть выполнен в какое-то неопределенное время, в некотором неопределенном потоке. Функция вернется к вызывающему абоненту, как только он будет установлен в очередь"
performBlockAndWait
: "Этот блок будет выполнен в течение некоторого неопределенного времени в этом же потоке. Функция вернется после полного выполнения этого кода (что произойдет после того, как текущая очередь, связанная с этим MOC, разрядилась)."
ИЗМЕНИТЬ
Вы уверены, что "executeBlockAndWait НЕ использует внутреннюю очередь"? Я думаю, что так и есть. Единственное различие заключается в том, что performBlockAndWait будет дождитесь завершения блока. И что вы имеете в виду, позвонив нить? По моему мнению, [moc performBlockAndWait] и [moc performBloc] запускаются в своей частной очереди (основной или основной). Важным понятием здесь является moc, которому принадлежит очередь, а не наоборот вокруг. Пожалуйста, поправьте меня, если я ошибаюсь. - Philip007
К сожалению, я сформулировал ответ так, как я, потому что, взятый сам по себе, это неверно. Однако в контексте исходного вопроса это правильно. В частности, при вызове performBlockAndWait
в частной очереди блок будет выполняться в потоке, вызывающем функцию, - он не будет помещен в очередь и выполнен в "частном потоке".
Теперь, прежде чем я даже углубился в детали, хочу подчеркнуть, что в зависимости от внутренней работы библиотек очень опасно. Все, что вам действительно нужно, это то, что вы никогда не сможете ожидать, что конкретный поток выполнит блок, за исключением всего, что связано с основным потоком. Таким образом, ожидая, что performBlockAndWait
не будет выполняться в основном потоке, не рекомендуется, потому что он будет выполняться в потоке, который его вызвал.
performBlockAndWait
использует GCD, но также имеет свой собственный слой (например, для предотвращения взаимоблокировок). Если вы посмотрите на код GCD (который является открытым исходным кодом), вы можете увидеть, как работают синхронные вызовы - и в общем случае они синхронизируются с очередью и вызывают блок в потоке, который вызывает эту функцию, - если очередь не является главной очередью или глобальная очередь. Кроме того, на переговорах WWDC инженеры Core Data подчеркивают, что performBlockAndWait
будет работать в вызывающем потоке.
Итак, когда я говорю, что он не использует внутреннюю очередь, это не означает, что он вообще не использует структуры данных. Он должен синхронизировать вызов с блоками, уже находящимися в очереди, и теми, которые представлены в других потоках и других асинхронных вызовах. Однако при вызове performBlockAndWait
он не помещает блок в очередь... вместо этого он синхронизирует доступ и запускает представленный блок в потоке, который вызывает функцию.
Теперь SO не является хорошим форумом для этого, потому что это немного сложнее, чем это, особенно w.r.t в главной очереди и глобальных очередях GCD, - но последнее не важно для Core Data.
Главное, что когда вы вызываете любую функцию performBlock*
или GCD, вы не должны ожидать, что она будет работать в любом конкретном потоке (за исключением того, что связано с основным потоком), потому что очереди не являются потоками, а только основной очереди будут запускать блоки по определенному потоку.
При вызове основных данных performBlockAndWait
блок будет выполняться в вызывающем потоке (но будет соответствующим образом синхронизирован со всем, отправленным в очередь).
Я надеюсь, что это имеет смысл, хотя это, вероятно, просто вызвало больше путаницы.
ИЗМЕНИТЬ
Кроме того, вы можете видеть невысказанные последствия этого, поскольку способ, которым performBlockAndWait
обеспечивает поддержку повторного входа, нарушает порядок блоков FIFO. В качестве примера...
[context performBlockAndWait:^{
NSLog(@"One");
[context performBlock:^{
NSLog(@"Two");
}];
[context performBlockAndWait:^{
NSLog(@"Three");
}];
}];
Обратите внимание, что строгое соблюдение гарантии FIFO очереди будет означать, что вложенные performBlockAndWait
( "Три" ) будут выполняться после асинхронного блока ( "Два" ), поскольку он был отправлен после отправки асинхронного блока. Однако это не так, как это было бы невозможно... по той же причине возникает тупик с вложенными вызовами dispatch_sync
. Просто что-то нужно знать, если вы используете синхронную версию.
В общем, избегайте синхронных версий, когда это возможно, потому что dispatch_sync
может вызвать тупик, а любая повторная версия, например performBlockAndWait
, должна будет принять какое-то "плохое" решение для ее поддержки... например, иметь версии синхронизации "прыгать" в очередь.