Параллелизм в среде Cocoa, Mac OS, iOS

Отправка источников (Dispatch Sources).

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

Отправка источников (Dispatch Sources) является одним из основных типов данных, который координирует обработку конкретных низкоуровневых системных событий. Grand Central Dispatch поддерживает следующие типы отправки источников:

  • Таймер отправки источника (Timer dispatch sources), генерирует периодические уведомления.
  • Сигнал отправки источников (Signal dispatch sources), уведомит Вас, когда поступает сигнал UNIX.
  • Дескриптор источников (Descriptor sources), уведомит Вас о различных операциях на основе файлов и сокетов, таких как:
    • Когда данные доступны для чтения
    • Когда возможна запись данных
    • Когда файлы удалены, перемещены или переименованы в файловой системе
    • Когда файловая мета-информации изменена
  • Процесс отправки источников (Process dispatch sources), уведомит Вас о связанных с процессом событиях, например:
    • Когда процесс завершается
    • Когда процесс запрашивантся вызовами fork или exec
    • Когда сигнал поступает на процесс
  • Mach порт отправки источников (Mach port dispatch sources) уведомит вас о связанных с Mach-ядром событиях
  • Пользовательские источники отправки (Custom dispatch sources), которые вы определите и запустите сами.

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

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

Для предотвращения перегрузки событий в очереди отправки, отправка источников реализует события по сливающейся схеме. Если новое событие прибывает до обработки предыдущего события, до того, как оно было извлечено из очереди и выполнено, отправка источника сливает данные из новых данных события с данными старого события. В зависимости от типа события, объединение может заменить старое событие или обновить информацию, которую оно занимает. Например, сигнал, на основе источника отправки дает информацию только о большинстве последних сигналов, а также сообщает, сколько всего сигналов было доставлено с момента последнего вызова обработчика события.

Создание Отправки источников

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

  1. Создайте источник отправки с помощью функции dispatch_source_create.
  2. Настройте источник отправки:
    • Назначте обработчик событий для отправки источника;
    • Для таймерного источника, установите таймер с помощью функции dispatch_source_set_timer;
    • При желании назначьте аннулирование обработчика источника отправки;
    • Вызовите функцию dispatch_resume, чтобы начать обработку событий;

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

Создание и установка обработчиков событий

Для обработки событий, генерируемых отправкой источника, вы должны определить обработчик событий для обработки этих событий. Обработчиком события является функция или блоковый объект, который вы установите на свой источник, с помощью отправки dispatch_source_set_event_handler или dispatch_source_set_event_handler_f функции. Когда событие приходит, источник отправки направляет, обработчик событий в указанную очередь отправки на обработку.

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

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

// Обработчик событий на основе блока
void (^dispatch_block_t)(void)

//Обработчик событий на основе функции
void (*dispatch_function_t)(void *)

Внутри обработчика события, вы можете получить информацию о данном событии от самого источника отправки. Хотя обработчикам событий на основе функции передается указатель на источник отправки в качестве параметра, блочногые обработчики событий должны захватить этот указатель сами. Вы можете сделать это для блоков с помощью ссылки на переменную, обычно содержащую источника отправки. Например, следующий фрагмент кода отражает источник в переменной, которая объявлена ​​вне рамок блока.

dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
           myDescriptor, 0, myQueue);

dispatch_source_set_event_handler(source, ^{
   // Получить данные из переменной источника, которая захватывается
   // из родительского контекста.
   
   size_t estimated = dispatch_source_get_data(source);

   // Продолжить чтение дескриптора ...
});

dispatch_resume(source);

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

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

ФункцияОписание
dispatch_source_get_handle
Эта функция возвращает базовую систему типов данных, управления источника отправки.

В качестве дескриптора источника отправки, эта функция возвращает число целого типа, содержащее дескриптор связанный с источником отправки.

Для сигнала источника отправки, эта функция возвращает число целого типа, содержащие номер сигнала для последнего события.

Для процесса источника отправки, эта функция возвращает pid_t структуру данных для подконтрольного процесса.

Для Mach порт источника отправки, эта функция возвращает mach_port_t структуру данных.

Для других источников отправки, значение, возвращаемое этой функцией не определено.

dispatch_source_get_data
Эта функция возвращает все необходимые данные, связанные с событием.

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

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

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

Для процесса отправки источника, эта функция возвращает константу, указывающую тип события, которое произошло. Список констант см. в разделе dispatch_source_proc_flags_t перечисленных типов.

Для Mach порт источника отправки, эта функция возвращает константу, указывающую тип события, которое произошло. Список констант см. в разделе dispatch_source_mach_send_flags_t перечисленных типов.

Для пользовательской отправки источника, эта функция возвращает новое значение данных, созданных из существующих данных, а также новые данные, переданные функции dispatch_source_merge_data.

dispatch_source_get_maskЭта функция возвращает флаги событий, которые были использованы для создания отправки источника.

Для источника процесса диспетчеризации, эта функция возвращает маску событий, котрую получает отправка источника. Список констант см. в разделе dispatch_source_proc_flags_t перечисленных типов.

Для отправки Mach порт при отправке источника прав, эта функция возвращает маску желаемого события. Список констант см. в разделе dispatch_source_mach_send_flags_t перечисленных типов.

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

Установка дескриптора отмены

Обработчики отмены используются для очистки отправки источника до его освобождения. Для большинства типов отправки источников, обработчики отмены являются необязательными и только если у вас есть собственные особенности поведения, связанные с отправкой источника, которые также должны быть обновлены. Для отправки источников, которые используют дескриптор или Mach порт, вы должны предоставить обработчик отмены, чтобы закрыть дескриптор или освободить Mach порт. Невыполнение этого требования может привести к тонким ошибкам в коде, в результате этого, при повторном использовании структур приведет к ошибке в коде или других частях системы.

Вы можете установить обработчик отмены в любое время, но обычно вы делатtе это при создании отправки источника. Вы устанавливаете обработчик отмены используя dispatch_source_set_cancel_handler или dispatch_source_set_cancel_handler_f функцию, в зависимости от того, что вы хотите использовать в реализации: объект блока или функцию. В следующем примере показан простой обработчик отмены, который закрывает дескриптор, открытый для отправки источника. Переменная fd является захваченной переменной, содержащей дескриптор.

dispatch_source_set_cancel_handler(mySource, ^{
   close(fd); // Close a file descriptor opened earlier.
});

Изменение целевой очереди

Хотя очередь, на которой запускается Ваше событие и обработчики отмены указываются при создании отправки источника, вы можете изменить эту очередь в любое время с помощью функции dispatch_set_target_queue. Это можно сделать, чтобы изменить приоритет, при котором обрабатываются события отправки источника.

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

Привязка пользовательских данных с помощью отправки источника

Как и многие другие типы данных в Grand Central Dispatch, вы можете использовать функцию dispatch_set_context для ассоциирования пользовательских данных с отправкой источника. Вы можете использовать контекстный указатель для хранения любых данных, обработчика событий необходимые для обработки событий. Если вы храните все пользовательские данные в контекстном указателе, вы должны также установить обработчик отмены (как описано в разделе "Установка дескриптора отмены"), чтобы освободить эти данные, когда отправки источник не нужен.

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

Управление памятью для отправки источников

Как и другие объекты отправки, отправка источников ведет подсчет ссылок типов данных. Отправка источника имеет начальный счетчик ссылок 1 и может быть сохранена и освобождена при помощи dispatch_retain и dispatch_release функций. Когда счетчик ссылок очереди достигнет нуля, система автоматически освобождает структуру данных отправки источника.

close

Блок объект Читать более подробно

Блок объекты - синтаксические и выполняемые функций C-уровня, которые позволяют составить функцию выражения, которая может быть переданы в качестве аргумента, при необходимости хранения и использования в многопоточных приложениях. Функция выражения может ссылаться и может сохранить доступ к локальным переменным. В других языках и средах, блок объекта иногда называют закрытием или лямбдой. Вы можете использовать блок, когда вы хотите создать рабочие модули (то есть сегменты кода), которые могут быть переданы как будто они являются значениями. Блоки предлагают более гибкое программирование и больше возможностей. Вы можете использовать их, например, для написания обратных вызовов или для выполнения операции для всех элементов в коллекции.

Объявление блока

Во многих случаях, можно использовать встроенные блоки, поэтому вам не нужно объявлять их. Синтаксиса декларации, однако, похож на стандартный синтаксис указателей на функции, за исключением того, что вы используете вставки (^) вместо звездочки указателя (*). Например, следующий код объявляет переменную aBlock, который ссылается на блок, который требует трех параметров и возвращает значение с плавающей точкой:

float (^aBlock)(const int*, int, float);

Создание блока

Вы можете использовать вставки (^) оператора для обозначения начала и точку с запятой, чтобы обозначить конец блока выражения. В следующем примере объявляется простой блок и относит его к ранее объявленной блоком переменной (oneFrom):

int (^oneFrom)(int);

oneFrom = ^(int anInt) {
    return anInt - 1;
};

Закрытие точкой с запятой требуется в качестве стандарта C конца линии маркера.

Если вы явно не объявляете возвращаемое значение выражения блока, оно может автоматически выводится из содержимого блока.

Блок-изменяемые переменные

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

Использование блоков

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

printf("%d\n", oneFrom(10));

Обычно, передают блок в качестве аргумента функции или методу. В этих случаях часто создают блок линию.

В следующем примере определяется, содержит ли объект NSSet слово, указанное в локальной переменной и задает значение другой локальной переменной (found) в YES (и прекращает поиск), если он выполняется. В этом примере, found объявлена ​​как переменная __block.

__block BOOL found = NO;
NSSet *aSet = [NSSet setWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil];
NSString *string = @"gamma";

[aSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
    if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {
        *stop = YES;
        found = YES;
    }
}];

// В этой точке, found == YES

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

Операции сравнения

Одной из наиболее распространенных операций, которые вы выполняете с блоками в среде Cocoa сравнение двух объектов, сортировка содержимого массива. Среда выполнения Objective-C определяет блок типа NSComparator для использования этих сравнений.

 
 
homeЗаметили ошибкукарта сайта 
   Made on a Mac