Dmitriy Azarov

Разработка многопоточных приложений в iOS / OS X

Параллелизм позволяет выполнять несколько операций одновременно. С развитием многоядерных процессоров появилась возможность запускать несколько приложений одновременно. Большинство приложений и программ, запущенных в фоне, выполняют некоторые операции и не требуют мощных вычислительных ресурсов. Другое дело - приложение на переднем плане, с которым работает пользователь. Это должно быть очень отзывчивое приложение, показывающее текущее состояние и не создающее ощущения зависания.

Ранее, при создании многопоточных / параллельных приложений необходимо было вручную создавать дополнительный поток. И многие знают, работа с потоками не самое приятное занятие. Потоки низкоуровневые. Угадать оптимальное число потоков тоже довольно трудная задача. И почему этим должен заниматься разработчик? Операционная система лучше меня знает сколько ресурсов у компьютера, какой процессор, сколько потоков будет оптимально для выполнения той или иной задачи. В дополнение, задача синхронизации между потоками тоже не очень приятная, сопряженная с риском облажаться.

Обе операционные системы iOS и OS X приняли более асинхронный подход к реализации задачи параллельного программирования. Они отошли от традиционных подходов, основанных на потоках. Вместо того, чтобы создавать дополнительный поток, предлагается создать определенную задачу и позволить операционной системе самой выполнить ее. Отдавая операционной системе волю управления потоками, приложения становятся более масштабируемые (между разными устройствами на одной платформе) и гораздо проще с точки зрения разработки и поддержки.

Grand Central Dispatch

Grand Central Dispatch — технология для запуска задач асинхронно. Эта технология берет на себя управление потоками. Вы пишете код, а система разбирается с потоками сама. Все что нужно сделать это создать задачу и добавить ее в очередь. GCD берет на себя заботу о создании необходимого количества потоков и планировщиком ваших задач, чтобы запустить их выполнения в созданных потоках. Поскольку теперь GCD управляет потоками, GCD обеспечивает целостный подход к управлению задачами, обеспечивая более высокую эффективность (в сравнении с потоками).

Dispatch Queues

Dispatch Queues — это механизм, реализованный на C, для выполнения произвольных задач. Эта очередь выполняет задачи либо последовательно, либо параллельно (ваш капитан), но всегда в порядке добавления. В последовательной очереди (serial dispatch queue) в один момент времени выполняется только одна задача, другие ожидают, пока не завершится выполнение этой задачи. В параллельной очереди задачи начинают выполняться так много, сколько может выполнить GCD в текущий момент. Следующая задача может не дожидаясь выполнения предыдущей начать выполняться.

Dispatch Queues имеет следующие достоинства:

  • простой интерфейс программирования
  • автоматическое управление потоками
  • занимают меньше памяти
  • не повредят систему под большой нагрузкой
  • задачи не могут заблокировать друг друга (взаимная блокировка)
  • не нужно управлять блокировками
  • очень просто масштабируются
  • serial dispatch queue предлагают более эффективную альтернативу lock и других примитивных механизмов синхронизации потоков

Рассмотрим подробнее Dispatch Queue

Существует 3 типа dispatch queue:

Serial

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

Concurrent

Concurrent queue (также известна как global dispatch queue) выполняет одну или более задач одновременно. Задачи также стартуют в порядке добавления в очередь. Текущие задачи выполняются в разных потоках, которые управляются самой очередью. Точное количество одновременно выполняемых задач изменяется самой системой и зависит от доступных ресурсов и других факторов.

Main Dispatch queue

Эта очередь доступно глобально всему приложению, которая является эквивалентом main thread. Эта очередь работает с run loop. Эта очередь обрабатывает пользовательский ввод.

GCD на Swift

Не будем рассматривать одно и то же. Рассмотрим примеры вызовов функций GCD. Для начала напишем несколько оберток:

var GlobalMainQueue: dispatch_queue_t {
  return dispatch_get_main_queue()
}
 
var GlobalUserInteractiveQueue: dispatch_queue_t {
  return dispatch_get_global_queue(Int(QOS_CLASS_USER_INTERACTIVE.value), 0)
}
 
var GlobalUserInitiatedQueue: dispatch_queue_t {
  return dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.value), 0)
}
 
var GlobalUtilityQueue: dispatch_queue_t {
  return dispatch_get_global_queue(Int(QOS_CLASS_UTILITY.value), 0)
}
 
var GlobalBackgroundQueue: dispatch_queue_t {
  return dispatch_get_global_queue(Int(QOS_CLASS_BACKGROUND.value), 0)
}

Теперь можно использовать планировщик следующим образом:

dispatch_async(GlobalUserInitiatedQueue) {
  let overlayImage = self.faceOverlayImageFromImage(self.image)
  dispatch_async(GlobalMainQueue) {
    self.fadeInNewImage(overlayImage)
  }
}

Dispatch Groups

Dispatch Groups уведомляют вас, когда все задачи выполнены. Эти задачи могут быть синхронными и асинхронными, например

//Загрузи все фотки и скажи когда будет готово
func downloadPhotosWithCompletion(completion: BatchPhotoDownloadingCompletionClosure?) {
  dispatch_async(GlobalUserInitiatedQueue) {
    var storedError: NSError!
    //Срздаем новую группу, которая считает кол-во незавершенных задач
    var downloadGroup = dispatch_group_create()
 
    for address in ["url1","url2,"url3"]
    {
      let url = NSURL(string: address)
      // dispatch_group_enter вручную уведомляем группу, что выполнение задачи началось. Вы должны следить за кол-вом dispatch_group_enter и dispatch_group_leave 
      dispatch_group_enter(downloadGroup)
      let photo = DownloadPhoto(url: url!) {
        image, error in
        if let error = error {
          storedError = error
        }
        dispatch_group_leave(downloadGroup) // Говорим, что задание выполнено
      }
      PhotoManager.sharedManager.addPhoto(photo)
    }
 
    dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER) // ждем бесконечность, пока наши задания не будут выполнены
    dispatch_async(GlobalMainQueue) { // тут они гарантированно будут все выполнены
      if let completion = completion { // вызываем колбэк дальше
        completion(error: storedError)
      }
    }
  }
}

 

  • 01 июн 2016
  • iOS, Grand Central Dispatch
0 комментариев
Ваш комментарий
адрес не будет опубликован
Текст