Процесс -- это экземпляр программы (.exe) во время выполнения, которому выделены системные ресурсы (процессорное время и память). Каждый процесс выполняется в отдельном адресном пространстве: один процесс не может получить доступ к данных другого. Для обмена информацией процессы могут использовать конвейеры (pipe), файлы, [сетевые каналы](47027.html) и другие способы межпроцессорного взаимодействия.
Потоки существуют внутри процесса и используют его ресурсы.
Поток (thread) -- независимый поток исполнения команд в рамках процесса.
Все потоки имеют доступ к общей глобальной памяти, а также могут иметь собственную память (thread_local).
*Для доступа к общим данным требуется синхронизация*.
Современные процессоры содержат от 4 (в ПК и мобильных телефонах) до 128 ядер (в серверах для облачных вычислений).
Кроме того, каждое ядро содержит несколько универсальных и специализированных АЛУ, что позволяет ядру выполнять несколько машинных команд за 1 такт или векторные команды с несколькими значениями одновременно (SSE/AVX), эмулировать дополнительное виртуальное ядро (hyper threading, увеличение производительности до 30%). В языках программирования нет способа явно использовать первые две возможности, но компиляторы могут сгенерировать команды при включении высокого уровня оптимизации.
Явным образом можно задать только распределение вычислений по нескольким ядрам процессора. Для этого можно использовать библиотеки POSIX для управления потоками (1995), OpenMP (1998) или библиотеки, входящие в стандарт языка программирования (2011).
При одновременной работе нескольких потоков необходимо учитывать следующие проблемы:
* изменение одной переменной несколькими потоками -- для каждого ядра есть 2 уровня кэш-памяти и новое значение переменной должно стать доступным для других ядер;
* обеспечение соответствия между значением переменной и другими переменными и/или предыдущим значением этой переменной -- между чтением данных для вычислений (в регистры) и записью результата присваивания может произойти изменение этих данных в другом потоке;
* доступ к ресурсам (файлам, сети, консоли и другим устройствам) -- чтение и запись информации производится связанными группами, пока группа действий не будет выполнена, другой поток не должен прерывать это взаимодействие;
* повторное вхождение в функцию в нескольких потоках (потокобезопасность) -- для функций, использующих глобальные переменные и ресурсы для вычислений.
Первая проблема решается самим процессором, когда из какого-либо кэша данные переписываются в оперативную память, контроллеры остальных ядер получают сигнал об этом изменении и, если необходимо, изменяют соответствующие данные в своих кэшах. Но компилятор должен знать о возможности обращения к переменной в других потоках и не использовать регистры для ускорения вычислений с этой переменной (спецификатор volatile).
Для решения трех других проблем в коде выделяются критические участки, а с важными переменными и ресурсами связывается семафор или мьютекс (вариант семафора, который может разблокировать только поток, выполнивший его блокировку). Перед выполнением критического участка мьютекс блокирует использование данных или ресурсов, а по окончании -- разблокирует.
При попытке блокировки уже заблокированного мьютекса другим потоком, поток приостанавливается и ставится в очередь, пока мьютекс не будет разблокирован первым потоком.
В простых случаях используются атомарные переменные, при изменении которых применяется специальная машинная команда, гарантирующая, что выполнению действия не помешают другие ядра процессора.
```c
int v1=0;
_Atomic int v2=0;
int main()
{ ++v1; // inc DWORD PTR v1
++v2; // lock inc DWORD PTR v2
}
```
В С++ используется специальный класс
> ``#include <atomic>``
> ``std::atomic<int> v2{0};``
Для упрощения переноса кода из C в С++ и обратно в заголовочных файлах ``<stdatomic.h>`` языка С и ``<atomic>`` языка C++ для всех базовых типов определены названия соответствующих атомарных типов: ``atomic_int``, ``atomic_llong``, ``atomic_char`` и т.д. Также в ``<stdatomic.h>`` определены вспомогательные функции для работы с атомарными переменными, аналоги атомарных машинных команд.
Семафоры реализуются с помощью атомарных переменных.