Skip to content

Latest commit

 

History

History
170 lines (113 loc) · 11.4 KB

3.2.1 Multithreading.md

File metadata and controls

170 lines (113 loc) · 11.4 KB

Многопоточность (multithreading)

  1. Concurrent Programming
  2. Multithreading and Concurrency

Предисловие

❗ Прежде чем приступать к изучению многопоточности нужно прочитать про Concurrency(параллельность) and Multitasking(многозадачность)

Очередь

Говоря о параллелизме (concurrency) в Swift, обычно имеют в виду очереди, которые содержат в себе thread (потоки).

Очередь — это массив задач, каждая из которых начинает выполнение в порядке их добавления.

Существует два типа очередей: последовательные(serial) и параллельные(concurrent).

  1. В последовательной (serial) очереди задачи выполняются одна за другой. Следующая задача не будет запущена, пока предыдущая не будет завершена.

Одновременно может выполняться только одна задача

  1. В параллельной (concurrent) очереди задачи также выполняются одна за другой. Однако каждая задача выполняется в отдельном потоке, и очередь не ждет, пока предыдущая задача запустит следующую. Следующая задача запускается немедленно, если ресурсов достаточно для создания еще одного потока.
SerialAndConcurrent

Sync Async

  1. Swift. Concurrency
SyncAsync

Разница между синхронными и асинхронными задачами, однопоточностью и многопоточностью

Ниже описаны термины в привязке к GCD, но с небольшими изменениями они верны и для программирования в целом.

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

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

Нужно запомнить, что синхронность-асинхронность и однопоточность-многопоточность две пары разных характеристить, и одни не зависят от других (и синхронность и асинхронность могут работать как в однопоточной среде, так и в многопоточной)

Способы достижения многопоточности в iOS и macOS

Существует три способа достижения параллелизма в iOS:

Последующие методы работы многопоточности в iOS - это всего лишь удобные обертки над unix потоками: судя по стеку вызовов NSThread использует pthread. Насчет dispatch он использует darwin-libpthread

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

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

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

Все dispatch queues представляют собой структуры данных FIFO, поэтому задачи всегда запускаются в том же порядке, в котором они добавлены.

В отличие от dispatch queue очереди операций (NSOperation Queue) не ограничиваются выполнением задач в порядке FIFO и поддерживают создание сложных графиков выполнения заказов для ваших задач.

Что такое мьютекс (mutex)?

Мьютекс — это семафор, работающий с системой блокировки

При написании многопоточных приложений требуется работать с общими данными из разных потоков и синхронизировать их. Для синхронизации потоков существуют объекты синхронизации - мьютекс (в iOS SDK они реализуются в виде NSLock и NSRecursiveLock).

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

Что такое семафор (semafor)?

Семафор позволяет выполнять какой-либо участок кода одновременно только конкретному количеству потоков. В основе семафора лежит счетчик, который и определяет, можно ли выполнять участок кода текущему потоку или нет. Если счетчик больше нуля — поток выполняет код, в противном случае — нет. В GCD выглядит так: semaphore_create – создание семафора (аналог sem_init) semaphore_destroy – удаление, соответственно (аналог sem_destroy) semaphore_wait – блокирующее ожидание на семафоре (аналог sem_wait) semaphore_signal – освобождение семафора (аналог sem_post)

Пример мьютекса - NSlock

Раскрыть

Пример, где можно использовать мьютекс NSLock:

var counter = 0 
let thread1 = Thread {
    for _ in 0..<1000 {
         counter += 1
    }
}

let thread2 = Thread {
    for _ in 0..<1000 {
         counter += 1
    }
}
thread1.start()
thread2.start()

// counter < 2000

Операция увеличения счетчика не атомарно. Оно состоит из нескольких ша:

let tmp = counter + 1
counter = tmp

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

var counter = 0
let lock = NSLock()
let thread1 = Thread {
    for _ in 0..<1000 {
         lock.lock()
         counter += 1
         lock.unlock()
    }
}

let thread2 = Thread {
    for _ in 0..<1000 {
         lock.lock()
         counter += 1
         lock.unlock()
    }
}
thread1.start()
thread2.start()

// counter < 2000

Участок кода между lock() и unlock() называется критическая секция. NSLock позволяет вызывать unlock() только тому потоку с которого был вызван lock().

Deadlock:

let lock = NSLock()
lock.lock()
lock.lock() // deadlock()

Исправление deadlock:

let lock = NSRecursiveLock()
lock.lock()
lock.lock()
lock.unlock()
lock.unlock()


3.2 Multithreading Theme Folder | Back To iOSWiki Contents | 3.2.2 Problems Of Multithreading Theme