Предлагает полную поддержку сопрограмм для языков iOS

Платформа разработки сопрограмм, разработанная для платформы iOS, coobjc от Alibaba обеспечивает поддержку сопрограмм для некоторых API в Foundation и UIKit через библиотеку cokit. Он поддерживает Objective-C и Swift.

Alibaba сделала coobjc доступным на Github под лицензией Apache с открытым исходным кодом. В этой статье дается обзор фреймворка, от его дизайна до вариантов использования.

Проблема асинхронного программирования

Асинхронное программирование продвигалось довольно медленно за 11 лет, прошедших с момента запуска первой платформы iOS в 2008 году.

В настоящее время обратный вызов асинхронного программирования на основе блоков является наиболее широко используемым методом асинхронного программирования iOS. Библиотека GCD, предоставляемая системой iOS, делает асинхронное программирование простым и удобным, но не лишено недостатков. Назвать несколько:

· Если вы не будете осторожны, вы попадете в ад обратных звонков

· Обработка ошибок - долгий и сложный процесс

· Легко забыть вызвать обработчик завершения

· Условное исполнение затруднено

· Получение комбинированных результатов по взаимно независимым звонкам чрезвычайно сложно

· Иногда непрерывное выполнение происходит в неправильном потоке (например, в пользовательском интерфейсе операции подпотока)

· Многопоточные сбои случаются часто, и причину определить сложно.

· Часто возникают проблемы с производительностью и сбои из-за неправильного использования блокировок и сигналов.

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

Фактически, эти проблемы являются обычным явлением при разработке многих систем и языков, и тенденция заключалась в их решении с помощью сопрограмм. Многие языки теперь поддерживают сопрограммы и связанный с ними синтаксис, включая C #, Kotlin, Python, Javascript и, с недавних пор, C ++. Это решает проблему асинхронного программирования для разработчиков, использующих эти языки.

Но как насчет Objective-C и Swift? Разработчикам, использующим эти языки, также нужен способ извлечь выгоду из нового опыта программирования, который поставляется с сопрограммами. Так было с мобильной командой Taobao в Alibaba, ответственной за приложение для электронной коммерции, которое обслуживает сотни миллионов пользователей в месяц.

Соответственно, команда разработчиков мобильной архитектуры Taobao потратила много времени на изучение библиотек нижнего уровня и языка ассемблера. Наконец, они появились с coobjc, надежным решением для поддержки сопрограмм Objective-C и Swift через язык ассемблера и язык C.

Все о Coobjc: от основных функций до исходного кода

Основные особенности Coobjc:

· Подобно программированию Async / Await, используемому в C # и Javascript, coobjc получает результаты выполнения асинхронного программирования, вызывая метод await. Это делает его идеальным выбором для выполнения и преобразования синхронизированных последовательностей, вызываемых вводом-выводом, сетью и другими трудоемкими асинхронными процессами.

· Coobjc предлагает функцию, аналогичную генератору Kotlin, что делает его идеальным для ленивых вычислений для генерации прерываемых многопоточных сериализованных данных.

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

· Coobjc поддерживает кортежи, которые позволяют разработчикам Objective-C использовать механизм, аналогичный множеству возвращаемых значений в Python.

Архитектура системы

Системная архитектура coobjc (показанная ниже) выглядит следующим образом:

· Нижний уровень - это ядро ​​сопрограмм, которое управляет переключением стека, реализует планирование сопрограмм и реализует канал связи между сопрограммами.

· Средний уровень - это оболочка, основанная на операторах сопрограммы. В настоящее время он поддерживает такие модели программирования, как async / await, Generator и Actor.

· Верхний уровень - это расширение сопрограммы к системной библиотеке, которое в настоящее время охватывает все операции ввода-вывода и трудоемкие методы Foundation и UIKit.

Coobjc также предлагает следующие встроенные расширения сопрограмм:

· NSArray, NSDictionary и другие библиотеки контейнеров: расширение сопрограммы помогает решать проблемы асинхронных вызовов при сериализации и десериализации.

· Объекты данных, такие как NSData, NSString и UIImage: расширение сопрограммы для решения проблем асинхронного вызова при чтении и записи ввода-вывода.

· NSURLConnection и NSURLSession: расширение сопрограммы решает проблемы с асинхронными вызовами во время асинхронных сетевых запросов.

· Библиотеки преобразователя, такие как NSKeyedArchieve и NSJSONSerialization: расширение сопрограммы решает проблемы с асинхронными вызовами на протяжении всего процесса решения.

Принципы работы

Основная философия сопрограммы - контролировать активную отдачу и возобновление стека вызовов.

Типичная реализация сопрограммы предлагает два важных действия:

· Выход: это означает передачу процессора, который прерывает текущее выполнение и возвращается в предыдущее состояние, в котором произошло возобновление.

· Resume: продолжает выполнение сопрограммы. Когда выполняется Resume, сопрограмма возвращается к предыдущей точке Yield.

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

Теоретически, как рассудили специалисты, есть пять способов сделать это:

· Вариант 1. Используйте сборку ucontext в glibc (библиотека Yunfeng).

· Вариант 2. Используйте ассемблерный код для переключения между контекстами (реализуя сопрограмму C) так же, как ucontext.

· Вариант 3. Используйте синтаксис switch-case языков C для реализации протопотоков.

· Вариант 4. Используйте setjmp и longjmp из языков Си.

· Вариант 5. Используйте компилятор для поддержки синтаксического сахара.

Варианты 3 и 4 могут реализовать переход, но не могут гарантировать, что состояние стека вызовов будет успешно сохранено. Таким образом, эти варианты отсутствуют в том, что касается надежной реализации сопрограмм. Создание настраиваемого компилятора, как в варианте 5, без официальной поддержки со стороны Apple привело бы к плохой универсальности, так что этого тоже нет. Между тем, ucontext Варианта 1 был удален в iOS и, таким образом, непригоден для использования.

Этот левый вариант B: имитация ucontext посредством компиляции.

Ядро симуляции ucontext - сохранить и возобновить стек вызовов через getContext и setContext. Для этого необходимо ознакомиться с соглашениями о вызовах в разных архитектурах ЦП, если компиляция должна работать на разных ЦП.

Пока что coobjc поддерживает armv7, arm64, i386 и x86_64 как на реальных устройствах iPhone, так и на симуляторах.

Пример кода

Давайте посмотрим на пример того, как coobjc используется при написании кода для простой функции. Здесь функция - это веб-сайт, запрашивающий загрузку изображений.

Для начала вот наиболее часто используемый код для этого сценария:

//Asynchronously loading data from the network
[NSURLSession sharedSession].configuration.requestCachePolicy = NSURLRequestReloadIgnoringCacheData;
    NSURLSessionDownloadTask *task = [[NSURLSession sharedSession] downloadTaskWithURL:url completionHandler:
                                      ^(NSURL *location, NSURLResponse *response, NSError *error) {
                                          if (error) {
                                              return;
                                          }

                                          //Parsing data in child threads and generating images                                         
                                          dispatch_async(dispatch_get_global_queue(0, 0), ^{
                                              NSData *data = [[NSData alloc] initWithContentsOfURL:location];
                                              UIImage *image = [[UIImage alloc] initWithData:data];
                                              dispatch_async(dispatch_get_main_queue(), ^{
                                                  //Dispatch to the main thread to view the image 
                                                  imageView.image = image;
                                              });
                                          });

                                      }];

Теперь вот код, реконструированный для сопрограмм с использованием библиотеки coobjc:

//Main thread creates coroutine
co_launch(^{
    //Asynchronous network data loading
    NSData *data = await([NSURLConnection async_sendAsynchronousRequest:request response:nil error:nil]);
    //Asynchronous image parsing
    UIImage *image = await([UIImage async_imageWithData:data]);
    //Show image
    imageView.image = image;
});

Мало того, что исходные 20 строк кода были сокращены вдвое, новый код имеет лучшую логику и удобочитаемость. В этом заключается настоящая привлекательность coobjc. С помощью реконструкции сопрограмм сложный асинхронный код можно преобразовать в последовательный вызов с лаконичной и понятной логикой.

Представление

Помимо краткости, логичности и удобочитаемости, coobjc намного превосходит традиционную многопоточность в приложениях с высоким уровнем параллелизма.

Стресс-тест был выполнен путем моделирования чтения данных с высоким уровнем параллелизма на iPhone 7 (iOS11.4.1) с использованием сопрограмм и традиционного многопоточного подхода соответственно. Общий размер файла составлял 20 МБ, а максимальное количество потоков, используемых подходом сопрограмм, было 4. Результаты тестирования представлены в таблице ниже.

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

Когда параллелизм превышает 1000, многопоточный подход начинает показывать аномалии распределения потоков, и многие параллельные задачи не выполняются. На самом деле, указанные в таблице ›20 с относятся к тому, что некоторые задачи просто невозможно выполнить.

Для сравнения, coobjc работает точно так, как задумано, в сценариях с высоким уровнем параллелизма.

В двух словах

В этой статье coobjc был рассмотрен с разных сторон. Подводя итог, вот основные преимущества, которые он предлагает разработчикам.

Краткость

· Coobjc имеет всего несколько операторов по сравнению с десятками операторов в адаптивном режиме. Нет ничего проще!

· Coobjc работает по простому основному принципу; вот почему во всей библиотеке сопрограмм всего несколько тысяч строк кода.

Легкость использования

· Очень мало интерфейсов, даже проще, чем GCD.

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

Ясность

· Coobjc позволяет писать асинхронную логику синхронно. Это наиболее естественный способ написания кода, который значительно снижает вероятность человеческой ошибки.

· Код, написанный с использованием сопрограмм, имеет гораздо более высокую читаемость, чем код, написанный с использованием вложенности блоков.

Представление

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

· Больше никаких сбоев и зависаний. Использование сопрограмм снижает злоупотребление блокировками и сигналами. За счет упаковки интерфейсов сопрограмм (таких как ввод-вывод), которые могут привести к блокировке, производительность и скорость приложения повышаются до максимума.

Последняя мысль

«Программы должны быть написаны для того, чтобы люди могли их читать, и только случайно - чтобы машины могли их выполнять».

- Абельсон и Сассман

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

(Оригинальная статья Alibaba’s Taobao Technology Team)

Alibaba Tech

Подробная информация о последних технологиях Alibaba из первых рук → Facebook: Alibaba Tech. Twitter: « AlibabaTech ».