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

Сюжет

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

Основы

Это то, что вики говорит о семафорах в информатике

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

Семафоры обеспечивают синхронизированный доступ к общему ресурсу. Как он предоставляет такой доступ?

Семафор по существу содержит:

  • Счетчик, который позволяет семафору знать, сколько потоков могут использовать ресурс одновременно.
  • Очередь для хранения списка потоков, которым требуется доступ к общему ресурсу.
  • Метод Wait () для запроса доступа к общему ресурсу
  • Метод Signal () для освобождения доступа к общему ресурсу

Когда семафор получает запрос wait (), он проверяет значение счетчика:

  • если счетчик равен нулю, он помещает поток в очередь.
  • в противном случае он обеспечивает доступ к общим ресурсам и уменьшает счетчик на 1.

Когда семафор получает запрос signal (), он проверяет, есть ли в очереди какие-либо потоки.

  • Если в очереди есть потоки, она удаляет поток из очереди и предоставляет доступ к общему ресурсу.
  • в противном случае он увеличивает счетчик на 1.

Семафор отправки

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

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

Сюжет продолжается

Ok! Это много теории. Давайте посмотрим код, используя аналогию с продуктовым магазином.

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

Пора увидеть это в действии:

Ok! Я понимаю, что это много. Разобьем его на части.

  1. Мы создаем параллельную очередь, используя DispatchQueue
  2. Мы создаем семафор, используя DispatchSemaphore со значением 1, что означает, что только один поток может получить доступ к общему ресурсу за раз.
  3. Мы создаем биллинговый автомат. Это закрытие billingMachine, которое принимает два параметра: сумму, подлежащую выставлению счета, и номер дорожки.
  4. Затем мы отправляем 3 разных покупателей на 3 разные дорожки, и сотрудники магазина на дорожках могут сканировать продукты и сообщать покупателю окончательную сумму.
  5. Если вы четко заметили, я помещаю поток на дорожке 1 в режим сна на 2 секунды, чтобы убедиться, что у меня достаточно времени для сотрудников магазина на дорожке 1 для сканирования товаров стоимостью 1000 долларов :)
  6. Я добавил semaphore.wait() перед вызовом billingMachineclosure и semaphore.signal() после вызова billingMachine Closure возвращает

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

Выход

Это тоже выглядит противно. Позвольте мне сломаться. Мы начинаем с пустой очереди семафоров и со значением счетчика 1

  1. Клиент 1 переходит на дорожку 1 (выполняется в потоке 4)
    Клиент 2 переходит на дорожку 2 (выполняется в потоке 5)
    Клиент 3 переходит на дорожку 3 (выполняется в потоке 6)
  2. Дорожка 2: подождите ()
    Покупатель 2 на дорожке 2 быстро сканирует свои продукты, и сотрудники нажимают кнопку зуммера. Так как никто другой не использует биллинговый автомат, персонал на дорожке 2 получает биллинговый автомат, и счетчик обновляется до 0.
  3. Дорожка 3: wait ()
    В этот момент персонал на дорожке 3 щелкает зуммером, однако, поскольку счетчик равен нулю, семафор помещает дорожку 3 в очередь и теперь дорожку 3 заморожен и не может выполнять никаких действий, пока не получит доступ к платежному автомату. Вот почему такие операции, как wait () никогда не должны выполняться в основном потоке.
  4. Дорожка 2: signal ()
    После того, как дорожка 2 завершает выставление счетов и освобождает биллинговый автомат, семафор предоставляет первый элемент в очереди, т. е. дорожка 3 - доступ к биллинговому аппарату.
  5. Дорожка 3: signal ()
    Как только дорожка 3 завершает выставление счетов и освобождает биллинговый автомат, семафор проверяет очередь, и в ней ничего нет, поэтому он просто обновляет счетчик. до 1
  6. Дорожка 1: подождите ()
    Сотрудники дорожки 1 нажимают зуммер. Так как никто другой не использует биллинговый автомат, персонал на дорожке 1 получает биллинг-автомат, и счетчик обновляется до 0.
  7. Дорожка 1: signal ()
    Дорожка 1 завершает выставление счетов и освобождает биллинговый автомат, семафор проверяет очередь, и в ней ничего нет, поэтому он просто обновляет счетчик на 1

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

Бонус

Вы когда-нибудь слышали о блокировке или мьютексе? Чем семафор отличается от них?

Семафор - это метод синхронизации, с помощью которого мы можем контролировать количество потоков для доступа к ресурсу. В режиме lock / Mutex только один поток может одновременно получать доступ к ресурсам. Но семафор позволяет нескольким потокам (значение счетчика) получать доступ к одному и тому же ресурсу одновременно.

Заключение

Это все, что у меня есть для этой статьи. Подпишитесь на меня в twitter, чтобы узнать больше. Аплодируйте, если вам понравился пост, поскольку это побудит меня написать больше (вы можете дать до 50). Не беспокойтесь о педантичности. Если вы видите, что я могу что-то улучшить в своей статье, дайте мне знать.