 # Многопоточность (multithreading)
 
1. [Concurrent Programming](https://www.oreilly.com/library/view/high-performance-ios/9781491910993/ch04.html)
2. [Multithreading and Concurrency](https://github.com/sashakid/ios-guide/blob/master/Main/10_multithreading_concurrency.md)

## Предисловие

> ❗ Прежде чем приступать к изучению многопоточности нужно прочитать про [Concurrency(параллельность) and Multitasking(многозадачность)](/2%20ComputerScience/2.0%20Linux/2.0.4%20ConcurrencyAndMultitasking/)

![](https://www.w3.org/People/Frystyk/thesis/MultiStackThread.gif)

## Очередь

Говоря о параллелизме ([concurrency](/2%20ComputerScience/2.0%20Linux/2.0.4%20ConcurrencyAndMultitasking/2.0.4.1%20Process.md)) в Swift, обычно имеют в виду очереди, которые содержат в себе thread ([потоки](/2%20ComputerScience/2.0%20Linux/2.0.4%20ConcurrencyAndMultitasking/2.0.4.2%20Thread.md)).

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

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

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

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

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

<div style="text-align: center;">
    <img src="https://github.com/eldaroid/iTWiki/blob/master/pictures/Concurrency/SerialAndConcurrent.png?raw=true" alt="SerialAndConcurrent" style="width: 60%; height: auto;">
</div>

## Sync Async

1. [Swift. Concurrency](https://maxim-kryloff.medium.com/swift-concurrency-810fdbe0b248)

<div style="text-align: center;">
    <img src="https://github.com/eldaroid/iTWiki/blob/master/pictures/Concurrency/SyncAsync.png?raw=true" alt="SyncAsync" style="width: 80%; height: auto;">
</div>

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

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

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

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

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


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

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

* [Потоки (threads)](/2%20ComputerScience/2.0%20Linux/2.0.4%20ConcurrencyAndMultitasking/2.0.4.2%20Thread.md);
* [GCD](./3.2.3%20GCD.md);
* NSOperationQueue;
* async await;

> Последующие методы работы многопоточности в iOS - это всего лишь удобные обертки над unix потоками: судя по стеку вызовов NSThread использует [pthread](/2%20ComputerScience/2.0%20Linux/2.0.4%20ConcurrencyAndMultitasking/2.0.4.2%20Thread.md). Насчет dispatch он использует darwin-libpthread

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

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

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

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

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

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

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

При написании многопоточных приложений требуется работать с общими данными из разных потоков и синхронизировать их. Для синхронизации потоков существуют объекты синхронизации - мьютекс (в iOS SDK они реализуются в виде [NSLock](/3%20Memory%20and%20Concurrency/3.2%20Multithreading/3.2.1%20Multithreading.md#пример-мьютекса---nslock) и NSRecursiveLock).

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

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

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

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

<details><summary>Раскрыть</summary>
<p>

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

```swift
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
```

Операция увеличения счетчика не [атомарно](/5%20Swift/5.2%20Glossary.md#:~:text=Атомарность). Оно состоит из нескольких ша: 

```swift
let tmp = counter + 1
counter = tmp
```

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


```swift
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](/3%20Memory%20and%20Concurrency/3.2%20Multithreading/3.2.2%20ProblemsOfMultithreading.md):

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

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

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

</p>
</details>

---

[3.2 Multithreading Theme Folder](/3%20Memory%20and%20Concurrency/3.2%20Multithreading/) | [Back To iTWiki Contents](https://github.com/eldaroid/iTWiki) | [3.2.2 Problems Of Multithreading Theme](./3.2.2%20ProblemsOfMultithreading.md)