Swift Language
совпадение
Поиск…
Синтаксис
Swift 3.0
DispatchQueue.main // Получить основную очередь
DispatchQueue (метка: «my-serial-queue», атрибуты: [.serial, .qosBackground]) // Создайте свою собственную приватную последовательную очередь
DispatchQueue.global (атрибуты: [.qosDefault]) // Доступ к одной из глобальных параллельных очередей
DispatchQueue.main.async {...} // Отправлять задачу асинхронно в основной поток
DispatchQueue.main.sync {...} // Отправлять задачу синхронно с основным потоком
DispatchQueue.main.asyncAfter (крайний срок: .now () + 3) {...} // Отправлять задачу асинхронно в основной поток, который будет выполняться через x секунд
Swift <3.0
dispatch_get_main_queue () // Получить основную очередь, запущенную в основном потоке
dispatch_get_global_queue (dispatch_queue_priority_t, 0) // Получить глобальную очередь с указанным приоритетом dispatch_queue_priority_t
dispatch_async (dispatch_queue_t) {() -> Void in ...} // Отправлять задачу асинхронно по указанному dispatch_queue_t
dispatch_sync (dispatch_queue_t) {() -> Void in ...} // Отправлять задачу синхронно на указанный dispatch_queue_t
dispatch_after (dispatch_time (DISPATCH_TIME_NOW, Int64 (наносекунды)), dispatch_queue_t, {...}); // Отправляем задачу на указанный dispatch_queue_t после наносекунд
Получение очереди Grand Central Dispatch (GCD)
Grand Central Dispatch работает над концепцией «Dispatch Queues». Очередь отправки выполняет задачи, которые вы назначаете в том порядке, в котором они передаются. Существует три типа диспетчерских очередей:
- Последовательные диспетчерские очереди (например, частные очереди отправки) выполняют одну задачу за раз, по порядку. Они часто используются для синхронизации доступа к ресурсу.
- Параллельные диспетчерские очереди (например, глобальные очереди отправки) выполняют одну или несколько задач одновременно.
- Основная очередь диспетчера выполняет задачи в основном потоке.
Для доступа к основной очереди:
let mainQueue = DispatchQueue.main
let mainQueue = dispatch_get_main_queue()
Система предоставляет параллельные глобальные очереди отправки (глобальные для вашего приложения) с различными приоритетами. Вы можете получить доступ к этим очередям, используя класс DispatchQueue
в Swift 3:
let globalConcurrentQueue = DispatchQueue.global(qos: .default)
эквивалентно
let globalConcurrentQueue = DispatchQueue.global()
let globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
В iOS 8 или более поздних версиях возможные значения качества обслуживания, которые могут быть переданы, являются .userInteractive
, .userInitiated
, .default
, .utility
и .background
. Они заменяют константы DISPATCH_QUEUE_PRIORITY_
.
Вы также можете создавать свои собственные очереди с различными приоритетами:
let myConcurrentQueue = DispatchQueue(label: "my-concurrent-queue", qos: .userInitiated, attributes: [.concurrent], autoreleaseFrequency: .workItem, target: nil)
let mySerialQueue = DispatchQueue(label: "my-serial-queue", qos: .background, attributes: [], autoreleaseFrequency: .workItem, target: nil)
let myConcurrentQueue = dispatch_queue_create("my-concurrent-queue", DISPATCH_QUEUE_CONCURRENT)
let mySerialQueue = dispatch_queue_create("my-serial-queue", DISPATCH_QUEUE_SERIAL)
В Swift 3 очереди, созданные с помощью этого инициализатора, по умолчанию являются серийными, а передача .workItem
для частоты автоопределения обеспечивает создание и удаление пула автозапуска для каждого рабочего элемента. Существует также .never
, что означает, что вы сами будете управлять собственными пулами автозапуска, или .inherit
который наследует настройку из среды. В большинстве случаев вы, вероятно, не будете использовать. .never
кроме случаев экстремальной настройки.
Выполнение задач в очереди Grand Central Dispatch (GCD)
Чтобы запускать задачи в очереди отправки, используйте методы sync
, async
и after
.
Чтобы отправить задачу в очередь асинхронно:
let queue = DispatchQueue(label: "myQueueName")
queue.async {
//do something
DispatchQueue.main.async {
//this will be called in main thread
//any UI updates should be placed here
}
}
// ... code here will execute immediately, before the task finished
Чтобы отправить задачу в очередь синхронно:
queue.sync {
// Do some task
}
// ... code here will not execute until the task is finished
Чтобы отправить задачу в очередь через определенное количество секунд:
queue.asyncAfter(deadline: .now() + 3) {
//this will be executed in a background-thread after 3 seconds
}
// ... code here will execute immediately, before the task finished
ПРИМЕЧАНИЕ. Любые обновления пользовательского интерфейса должны быть вызваны в основной поток! Убедитесь, что вы разместили код для обновлений пользовательского интерфейса внутри
DispatchQueue.main.async { ... }
Типы очереди:
let mainQueue = dispatch_get_main_queue()
let highQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
let backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)
Чтобы отправить задачу в очередь асинхронно:
dispatch_async(queue) {
// Your code run run asynchronously. Code is queued and executed
// at some point in the future.
}
// Code after the async block will execute immediately
Чтобы отправить задачу в очередь синхронно:
dispatch_sync(queue) {
// Your sync code
}
// Code after the sync block will wait until the sync task finished
Чтобы отправить задание через промежуток времени (используйте NSEC_PER_SEC
для преобразования секунд в наносекунды):
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
// Code to be performed in 2.5 seconds here
}
Чтобы выполнить задачу асинхронно и обновить пользовательский интерфейс:
dispatch_async(queue) {
// Your time consuming code here
dispatch_async(dispatch_get_main_queue()) {
// Update the UI code
}
}
ПРИМЕЧАНИЕ. Любые обновления пользовательского интерфейса должны быть вызваны в основной поток! Убедитесь, что вы разместили код для обновлений пользовательского интерфейса внутри
dispatch_async(dispatch_get_main_queue()) { ... }
Контурные петли
GCD обеспечивает механизм для выполнения цикла, посредством чего петли происходят одновременно друг с другом. Это очень полезно при выполнении серии дорогостоящих вычислений.
Рассмотрим этот цикл:
for index in 0 ..< iterations {
// Do something computationally expensive here
}
Вы можете выполнять эти вычисления одновременно с помощью concurrentPerform
(в Swift 3) или dispatch_apply
(в Swift 2):
DispatchQueue.concurrentPerform(iterations: iterations) { index in
// Do something computationally expensive here
}
dispatch_apply(iterations, queue) { index in
// Do something computationally expensive here
}
Закрытие цикла будет вызываться для каждого index
от 0
до, но не включая, iterations
. Эти итерации будут выполняться одновременно друг с другом, и, следовательно, порядок, в котором они выполняются, не гарантируется. Фактическое количество итераций, которые происходят одновременно в любой момент времени, обычно определяется возможностями соответствующего устройства (например, сколько ядер имеет устройство).
Несколько особых соображений:
Параметр
concurrentPerform
/dispatch_apply
может запускать циклы одновременно друг относительно друга, но все это происходит синхронно по отношению к потоку, из которого вы его вызываете. Поэтому не называйте это из основного потока, так как это блокирует этот поток до тех пор, пока цикл не будет выполнен.Поскольку эти петли происходят одновременно друг с другом, вы несете ответственность за обеспечение безопасности потоков результатов. Например, если обновить какой-либо словарь с результатами этих дорогостоящих вычислений, убедитесь, что вы сами синхронизировали эти обновления.
Обратите внимание: в запуске параллельных циклов есть некоторые служебные данные. Таким образом, если вычисления, выполняемые внутри цикла, недостаточно интенсивно вычислительны, вы можете обнаружить, что любая производительность, получаемая при использовании параллельных циклов, может быть уменьшена, если не быть полностью компенсированной, служебными данными, связанными с синхронизацией всех этих параллельных потоков.
Таким образом, вы отвечаете за определение правильного количества работы, которое должно выполняться на каждой итерации цикла. Если вычисления слишком просты, вы можете использовать «шагание», чтобы включить больше работы за цикл. Например, вместо выполнения параллельного цикла с 1 миллионом тривиальных вычислений вы можете выполнить 100 итераций в своем цикле, выполняя 10 000 вычислений на цикл. Таким образом, в каждом потоке выполняется достаточная работа, поэтому накладные расходы, связанные с управлением этими параллельными циклами, становятся менее значительными.
Выполнение задач в OperationQueue
Вы можете думать о OperationQueue
как о линии задач, ожидающих исполнения. В отличие от диспетчерских очередей в GCD, очереди операций не являются FIFO (first-in-first-out). Вместо этого они выполняют задачи, как только они готовы к выполнению, если для этого достаточно системных ресурсов.
Получить главный OperationQueue
:
let mainQueue = OperationQueue.main
Создайте пользовательский режим OperationQueue
:
let queue = OperationQueue()
queue.name = "My Queue"
queue.qualityOfService = .default
Качество обслуживания определяет важность работы или насколько пользователь, вероятно, рассчитывает на немедленные результаты этой задачи.
Добавить Operation
в OperationQueue
:
// An instance of some Operation subclass
let operation = BlockOperation {
// perform task here
}
queue.addOperation(operation)
Добавьте блок к OperationQueue
:
myQueue.addOperation {
// some task
}
Добавьте несколько Operation
s в OperationQueue
:
let operations = [Operation]()
// Fill array with Operations
myQueue.addOperation(operations)
Отрегулируйте, сколько Operation
s может выполняться одновременно в очереди:
myQueue.maxConcurrentOperationCount = 3 // 3 operations may execute at once
// Sets number of concurrent operations based on current system conditions
myQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount
Приостановка очереди предотвратит запуск каких-либо существующих, нераспределенных операций или любых новых операций, добавленных в очередь. Способ возобновления этой очереди заключается в том, isSuspended
вернуть isSuspended
в значение false
:
myQueue.isSuspended = true
// Re-enable execution
myQueue.isSuspended = false
Приостановка OperationQueue
не останавливает или отменяет выполняемые операции. Нужно только попытаться приостановить создавшуюся очередь, а не глобальные очереди или основную очередь.
Создание высокоуровневых операций
Структура Foundation предоставляет тип Operation
, который представляет объект высокого уровня, который инкапсулирует часть работы, которая может выполняться в очереди. Координация не только координирует работу этих операций, но также позволяет устанавливать зависимости между операциями, создавать отмененные операции, ограничивать степень параллелизма, применяемую в очереди операций, и т. Д.
Operation
готова к выполнению, когда все ее зависимости завершены. Свойство isReady
затем изменяется на true
.
Создайте простой неавтоматический подкласс Operation
:
class MyOperation: Operation {
init(<parameters>) {
// Do any setup work here
}
override func main() {
// Perform the task
}
}
class MyOperation: NSOperation {
init(<parameters>) {
// Do any setup work here
}
override func main() {
// Perform the task
}
}
Добавьте операцию в OperationQueue
:
myQueue.addOperation(operation)
Это будет выполнять операцию одновременно в очереди.
Управление зависимостями от Operation
.
Зависимости определяют другие Operation
s, которые должны выполняться в очереди до того, как Operation
считается готовой к выполнению.
operation2.addDependency(operation1)
operation2.removeDependency(operation1)
Запуск Operation
без очереди:
operation.start()
Зависимости будут проигнорированы. Если это параллельная операция, задача может выполняться одновременно, если ее метод start
работает в фоновых очередях.
Параллельные операции.
Если задача, которую выполняет Operation
, сама, асинхронная (например, URLSession
данных URLSession
), вы должны реализовать Operation
как параллельную операцию. В этом случае ваша isAsynchronous
реализация должна возвращать значение true
, обычно у вас есть метод start
который выполняет некоторую настройку, а затем вызывает его main
метод, который фактически выполняет задачу.
При isExecuting
асинхронной Operation
вы должны реализовать isExecuting
, isFinished
methods и isFinished
. Таким образом, при запуске isExecuting
свойство isExecuting
изменяется на true
. Когда Operation
завершает свою задачу, isExecuting
устанавливается в false
, а isFinished
- true
. Если операция отменена, оба isCancelled
и isFinished
изменяются на true
. Все эти свойства являются наблюдаемыми по ключевым значениям.
Отмените Operation
.
Вызов cancel
просто изменяет свойство isCancelled
на true
. Чтобы отреагировать на отмену в пределах вашего собственного подкласса Operation
, вы должны проверить значение isCancelled
по крайней мере, периодически в пределах main
и соответствующим образом реагировать.
operation.cancel()