Threads posix win32 что это
Threads posix win32 что это
Есть порты MinGW для 32-битной и для 64-битной версий Windows. Установка 64-битной версии имеет несколько не очень понятных опций настройки, которые специально рассмотрены в этой статье.
[Процесс установки MinGW-W64 по шагам]
2. На первом экране появится приветствие мастера установки, просто Кликните Next.
3. Затем появляется важный экран настройки, где нужно выбрать опции системы.
Architecture: i686 или x86_64. Выберите x86_64, если у Вас современный процессор Athlon или Intel. Если у Вас старый Pentium Pro, Pentium II или Pentium III, то выберите i686.
Exception: dwarf, sjlj или seh. Это относится к методике обработки ошибок. Состав выпадающего списка зависит от предыдущих опций.
Dwarf-2 EH. Реализация для Windows сама по себе не разработана для работы в условиях исполнения 64-битны приложений Windows. В режиме win32 вызываемый обработчик исключения не может распространяться на код, не совместимый со стандартом dw2; это означает, что любое исключение, которое происходит через любые «чужие фреймы» non-dw2 приведет к отказу, включая системные DLL Windows и DLL, встроенные в Visual Studio. Раскручивание кода Dwarf-2 в gcc включает инспектирование сборки x86, и оно не может происходить без соответствующей отладочной информации dwarf-2.
GCC поддерживает два метода обработки исключений (EH):
DWARF-2 (DW2) EH, который требует наличия в выполняемом коде отладочной информации DWARF-2 (или DWARF-3). DW-2 EH может привести к некоторому разрастанию размера исполняемого кода, потому что в код добавляется большие таблицы отката по содержимому стека.
SJLJ. Этот метод базируется на setjmp/longjmp (SJLJ). Он несколько медленнее, чем DW2 EH (влияет даже на нормальное выполнение, когда не срабатывают исключения), однако он может работать поверх кода, который не был скомпилирован GCC, или в котором нет информации о истории вызовов через стек (call-stack unwinding information).
Structured Exception Handling (SEH). Windows использует свой собственный механизм обработки исключений, известный как Structured Exception Handling (SEH). К сожалению, GCC пока его не поддерживает. Однако обещается, что для x64 в появится поддержка SEH начиная с версии GCC 4.8.
Exception handling models of GCC site:stackoverflow.com
C++ Exception Handling for IA-64 site:static.usenix.org
EH newbies howto gcc.gnu.org
MinGW 64 bit Exception handling: SJLJ, DWARF, and SEH site:qt-project.org
Я выбрал для своей установки вариант seh.
4. На этом шаге нужно просто выбрать каталог установки. Оставьте все по умолчанию.
5. Запустится процесс закачки, который займет некоторое время.
По окончании загрузки нажмите Next и после окончания копирования Finish.
Если Вы хотите, чтобы утилиты из каталога c:\Program Files\mingw-w64\x86_64-4.9.2-win32-seh-rt_v3-rev1\mingw64\bin\ запускались без ввода полного пути, то добавьте этот каталог в пути поиска (в переменную окружения %Path%).
Национальная библиотека им. Н. Э. Баумана
Bauman National Library
Персональные инструменты
POSIX Threads
Communications protocol | |
Purpose | позволяет программе контролировать множество разных потоков, накладывающихся во время |
---|---|
Developer(s) | POSIX.1c, Threads extensions (IEEE Std 1003.1c-1995) |
Introduced | 1995 ; 26 years ago ( 1995 ) |
Based on | C |
Influenced | POSIX 1003.1-2001 |
Содержание
Причины использования потоков
В традиционных операционных системах у каждого процесса есть адресное пространство и единственный поток управления. Фактически это почти что определение процесса. Тем не менее нередко возникают ситуации, когда неплохо было бы иметь в одном и том же адресном пространстве несколько потоков управления, выполняемых квазипараллельно, как будто они являются чуть ли не обособленными процессами (за исключением общего адресного пространства).
Основная причина использования потоков заключается в том, что во многих приложениях одновременно происходит несколько действий, часть которых может периодически быть заблокированной. Модель программирования упрощается за счет разделения такого приложения на несколько последовательных потоков, выполняемых в квазипараллельном режиме. Рассматривая потоки, мы добавляем новый элемент: возможность использования параллельными процессами единого адресного пространства и всех имеющихся данных. Эта возможность играет весьма важную роль для тех приложений, которым не подходит использование нескольких процессов (с их раздельными адресными пространствами).
Вторым аргументом в пользу потоков является легкость (то есть быстрота) их создания и ликвидации по сравнению с более «тяжеловесными» процессами. Во многих системах создание потоков осуществляется в 10–100 раз быстрее, чем создание процессов. Это свойство особенно пригодится, когда потребуется быстро и динамично изменять количество потоков. [Источник 1]
Третий аргумент в пользу потоков также касается производительности. Когда потоки работают в рамках одного центрального процессора, они не приносят никакого прироста производительности, но когда выполняются значительные вычисления, а также значительная часть времени тратится на ожидание ввода-вывода, наличие потоков позволяет этим действиям перекрываться по времени, ускоряя работу приложения.
И наконец, потоки весьма полезны для систем, имеющих несколько центральных процессоров, где есть реальная возможность параллельных вычислений.
Основные функции
В стандарте определено более 60 вызовов функций. Они могут быть разделены на 4 категории:
Опишем ряд самых основных функций, чтобы дать представление о том, как они работают.
Вызовы, связанные с потоком | Описание |
---|---|
pthread_create | Создание нового потока |
pthread_exit | Завершение работы вызвавшего потока |
pthread_join | Ожидание выхода из указанного потока |
pthread_yield | Освобождение центрального процессора, позволяющее выполняться другому потоку |
pthread_attr_init | Создание и инициализация структуры атрибутов потока |
pthread_attr_destroy | Удаление структуры атрибутов потока |
Два следующих вызова функций, связанных с потоками, относятся к атрибутам. Функция pthread_attr_init создает структуру атрибутов, связанную с потоком, и инициализирует ее значениями по умолчанию. Эти значения (например, приоритет) могут быть изменены за счет работы с полями в структуре атрибутов.
И наконец, функция pthread_attr_destroy удаляет структуру атрибутов, принадлежащую потоку, освобождая память, которую она занимала. На поток, который использовал данную структуру, это не влияет, и он продолжает свое существование.
Чтобы лучше понять, как работают функции пакета Pthread, рассмотрим простой пример, показанный ниже. Основная программа этого примера работает в цикле столько раз, cколько указано в константе NUMBER_OF_THREADS (количество потоков), создавая при каждой итерации новый поток и предварительно сообщив о своих намерениях. Если создать поток не удастся, она выводит сообщение об ошибке и выполняет выход. После создания всех потоков осуществляется выход из основной программы. При создании поток выводит однострочное сообщение, объявляя о своем существовании, после чего осуществляет выход. Порядок, в котором выводятся различные сообщения, не определен и при нескольких запусках программы может быть разным.
Мьютексы в пакете Pthreads
pthread_mutex_init | Создание мьютекса |
---|---|
pthread_mutex_destroy | Уничтожение существующего мьютекса |
pthread_mutex_lock | Овладение блокировкой или блокирование потока |
pthread_mutex_trylock | Овладение блокировкой или выход с ошибкой |
pthread_mutex_unlock | Разблокирование |
Условные переменные
Наиболее важные вызовы, связанные с условными переменными, показаны в таблице. В ней представлены вызовы, предназначенные для создания и уничтожения условных переменных.
Вызов из потока | Описание |
---|---|
pthread_cond_init | Создание условной переменной |
pthread_cond_destroy | Уничтожение условной переменной |
pthread_cond_wait | Блокировка в ожидании сигнала |
pthread_cond_signal | Сигнализирование другому потоку и его активизация |
pthread_cond_broadcast | Сигнализирование нескольким потокам и активизация всех этих потоков |
Условные переменные и мьютексы всегда используются вместе. Схема для одного потока состоит в блокировании мьютекса, а затем в ожидании на основе значения условной переменной, если поток не может получить то, что ему требуется. Со временем другой поток подаст ему сигнал, и он сможет продолжить работу. Вызов pthread_cond_wait осуществляется неделимо и выполняет разблокирование удерживаемого мьютекса как одну неделимую и непрерываемую операцию. По этой причине мьютекс является одним из его аргументов.
В качестве примера использования мьютексов и условных переменных в листинге ниже показана очень простая задача производителя-потребителя, в которой используется один единственный буфер. Когда производитель заполняет буфер, то перед тем как произвести следующую запись, он должен ждать, пока потребитель не опустошит этот буфер. Точно так же, когда потребитель извлечет запись, он должен ждать, пока производитель не произведет другую запись. При всей своей предельной простоте этот пример иллюстрирует работу основного механизма. Оператор, приостанавливающий работу потока, прежде чем продолжить его работу, должен убедиться в выполнении условия, поскольку поток может быть активирован в результате поступления сигнала UNIX или по другой причине.
Блокировка чтения-записи
Итак, в случае если нить пытается захватить блокировку на чтение и та не захвачена сейчас на запись (либо она свободна, либо уже захвачена на чтение) то захват ей удается и она продолжает свое выполнение, если же блокировка уже захвачена на запись то она ожидает ее освобождения. Если же нить хочет захватить блокировку на запись, то есть в эксклюзивном режиме, то она ожидает освобождения блокировки если та захвачена на чтение или на запись, и получает захват только если блокировка полностью свободна. Возникает вопрос кто получает блокировку если та освобождается и одновременно есть и ждущие писатели и ждущие читатели? Возможно, два варианта в случае если будет удовлетворена блокировка писателя, то можно говорить о предпочтении писателей, если же блокировку получат читатели, то можно говорить о предпочтении читателей. Оба варианта вполне разумны и полезны в разных ситуациях, первый, например, предпочтителен, в случае если мы хотим чтобы наши данные как можно скорее обновились и именно обновленными данными пользовались читатели, а второй удобен когда наша цель приоритетное обслуживание читателей а актуальность данных не так уж важна.
При этом согласно стандарту блокировка будет установлена, если нет активной блокировки по записи и нет нитей ожидающих возможности установки блокировки записи. В других случаях нить будет спать до появления возможности установить блокировку. Таким образом, блокировки чтения/записи предлагаемые стандартом это блокировки с предпочтением писателей, так как читатели всегда будут стараться уступать им дорогу. Установить блокировку записи можно при помощи функции:
Эта функция как и следует ожидать устанавливает блокировку записи в случае, если для данного (блокировка чтения/записи) не установлено никаких других блокировок, в остальных случаях вызвавшая ее нить будет ожидать освобождения блокировки. Нить может снять любую установленную ею ранее блокировку при помощи функции:
При этом, поскольку в данный момент времени для данной блокировки чтения/записи данная нить может обладать либо только правом чтения, либо правом записи, то при снятии блокировки всегда известно, какого именно типа блокировку мы сейчас снимаем, и соответственно эта функция не нуждается в вариантах. И, наконец, как и в случае мьютекса ресурсы занимаемые свободной блокировкой чтения/записи могут быть освобождены при помощи функции:
Барьеры
Где последний параметр count задает число нитей, которые должны дойти до барьера (вызвать функцию pthread_barrier_wait ) прежде чем они смогут продолжить выполнение. Раз существует функция инициализирующая барьер, то существует и комплиментарная функция которая уничтожает его:
Как и в случае остальных примитивов синхронизации данная функция должна вызываться только в случае если на уничтожаемом барьере не ожидает ни одной нити. Ну и наконец существует функция вызов которой собственно и соответствует преодолению барьера:
Спинлок
Третий примитив синхронизации, про который хотелось бы поговорить это spin lock(спинлок). С точки зрения функциональности этот примитив ничем не отличается от мьютекса. Основная стоящая за ними идея состоит в том, чтобы вместо того чтобы заблокировать нить в случае если блокировка уже захвачена, крутиться, проверяя не освободилась ли блокировка (отсюда и слово spin в названии). Фактически спинлок это мьютекс основанный на использовании активного ожидания. Какой тогда смысл в таком примитиве? В случае если мы имеем мультипроцессор (так как на однопроцессорной машине спинлок будет бесполезно съедать циклы процессора до тех пор, пока не произойдет переключение на другую владеющую блокировкой нить) и крайне малый размер критической секции защищаемой этим примитивом (чтобы опять же не ждать долго и снизить опасность переключения на другую нить) спинлок может быть использован эффективнее чем мьютекс. Это обусловлено тем что он не вызывает долгой операции перевода нити в сон, вообще считается что spin lock реализуется на основе самых быстрых механизмов синхронизации доступных в системе. В случае если какое-либо из двух вышеприведенных условий нарушается, спинлок не даст выигрыша в производительности и выгоднее воспользоваться мьютексом. Согласно стандарту данный примитив представляется при помощи типа pthread_spinlock_t и набора операций над ним, которые практически совпадают с соответствующими операциями для мьютекса:
Заключение
Подводя итог, хочется сказать, что pthreads используются, чтобы продолжать вычисления во время ожидания медленных или блокирующих операций, для проектирования модульных программ, явно выражая независимость тех или иных событий в программе, для эффективного использования параллелизма на многопроцессорных системах.
темы mingw-w64: posix vs win32
Я устанавливаю mingw-w64 на Windows, и есть два варианта: win32 threads и posix threads. Я знаю, в чем разница между потоками win32 и pthreads, но я не понимаю, в чем разница между этими двумя вариантами. Я сомневаюсь, что если я выберу потоки posix, это помешает мне вызывать функции WinAPI, такие как CreateThread.
кажется, что эта опция указывает, какой API потоков будет использоваться некоторой программой или библиотекой, но чем? По НКУ, libstdc++ или что-то еще?
Я нашел это: в чем разница между thread_posixs и thread_win32 в порту gcc windows?
короче говоря, для этой версии mingw выпуск threads-posix будет использовать API posix и разрешать использование std::thread, а threads-win32 будет использовать API win32 и отключит часть std::thread стандарта.
Ok, если я выберу win32 threads, то std:: thread будет недоступно, но потоки win32 по-прежнему будут использоваться. Но используется кем?
3 ответов
Я должен подчеркнуть, что эта опция не запрещает вам писать любой код, который вы хотите (он имеет абсолютно нет влияние на то, какой API вы можете вызвать в своем коде). Он отражает только то, что библиотеки времени выполнения GCC (libgcc / libstdc++/. ) использовать для их функциональности. Оговорка, цитируемая @James, не имеет ничего общего с внутренней моделью потоковой передачи GCC, а скорее с реализацией CRT Microsoft.
ни имеют влияние на любой пользовательский код, вызывающий Win32 APIs или Pthreads APIs. Вы всегда можете использовать оба.
части среды выполнения GCC (в частности, обработка исключений) зависят от используемой модели потоков. Таким образом, если вы используете версию среды выполнения, которая была построена с потоками POSIX, но решили создать потоки в своем собственном коде с API Win32, в какой-то момент у вас могут возникнуть проблемы.
даже если вы используете потоковую версию Win32 среды выполнения, вы, вероятно, не должны вызывать API Win32 напрямую. Цитата из компилятор MinGW FAQ:
обратите внимание, что теперь можно использовать некоторые из C++11 std::thread в режиме Win32 threading. Эти адаптеры только для заголовков работали из коробки для меня: https://github.com/meganz/mingw-std-threads
из истории ревизий похоже, что есть какая-то недавняя попытка сделать это частью среды выполнения mingw64.
Универсальные потоки на С++ для Windows и UNIX
Потоки (threads) являются весьма удобным механизмом для ускорения программ и придания им гибкости в использовании процессорного времени, особенно в наш успешно и бесповоротно наступивший век многоядерных процессоров, стоящих почти в каждом современном компьютере. Чего уж говорить о серверных платформах.
Итак, задался я целью иметь удобный и простой класс на С++ для работы с потоками. В силу особенностей работы мне приходится иметь дело различными системами, и хотелось иметь максимально переносимый вариант. На сегодняшний день стандартом де-факто для мира UNIX являются так называемые потоки POSIX Для Windows тоже есть реализация этой библиотеки, но в целях исключения дополнительной внешней зависимости для этой платформы я решил пользоваться напрямую Windows API, благо назначения функций очень похожи. При использования POSIX Threads под Windows данный класс еще упрощается (надо просто выкинуть всю Windows секцию), но для меня лично удобнее было не иметь зависимости от виндусовых POSIX Threads. Дополнительная гибкость, так сказать.
Для компиляции в Windows необходимо определить макрос WIN32. В этом случае будет использоваться Windows API. Иначе подразумевается работа с pthreads. Если вы используете Cygwin, то можно работать и через Windows API и через pthreads.
Возникает резонный вопрос — я почему ни один из вызовов функций не проверяет код ошибки. Вдруг что? Я могу сказать, что я встретил только один случай возврата ошибки от pthread_create.
Это было на AIX’e при использовании связывания (linking) времени исполнения. Программа не была слинкована с библиотекой pthreads (я забыл указать ключик “-lpthread”), но из-за особенностей линковки времени исполнения (так любимой AIX’ом) линкер сообщил, что все хорошо и выдал мне исполняемый файл. В процессе же работы ни одна функция из библиотеки pthreads просто не вызывалась. Интересно, что код ошибки функции pthread_create() означал что-то типа “не могу открыть файл”, и чего я сделал вывод, что файл библиотеки недоступен. Вообще, линковка времени исполнения — это довольно хитрая штука. В данном виде связывания внешние связи определены уже на стадии линковки (то есть это не тоже самое, что загрузка разделяемой библиотеки вручную во время работы, самостоятельный поиск функций по именам и т.д.), но вот фактический поиск вызываемой функции происходит в сам момент старта программы. Получается, что до непосредственно запуска нельзя проверить в порядке ли внешние зависимости (команда ldd рапортует, что все хорошо). Более того, разрешение внешних зависимостей происходить в момент вызовы внешней функции. Это довольно гибкий механизм, но вот его практическая полезность пока остается для меня загадкой. Вообще AIX является довольно изощренной системой в плане разнообразия механизмов связывания. Позже я постараюсь описать результаты моих “исследований” AIXа на эту тему.
Но вернемся к причинам отсутствия проверки кодов возврата от функций pthreads и Windows API. Как я уже упомянул, если какая-то из этих функций завешается с ошибкой, то с огромной вероятностью что-то радикально не так в системе, и это не просто нормальное завершение функции с ошибкой, после которой можно как-то работать дальше. Это фатальная ошибка, и ваше приложение не будет работать нормально еще по туче других причин. Кроме этого я хотел сделать это класс максимально простым, чтобы его можно было таскать из проекта в проект и не допиливать его напильником под существующую в проекте систему обработки ошибок (исключения, коды возврата, журналирование и т.д.), так как в каждом проекте она может быть разная.
Читатель всегда может добавить в код необходимые проверки для собственных нужд.
Кроме этого, я всегда использую в разработке unit-тестирование, и данный класс также имеет тесты. Поэтому запускаемый при каждой полной сборке проекта набор тестов сразу выявляет большинство проблем (уже проблемы линковки точно).
В следующей главе я расскажу про технику использования описанного класса — как создавать потоки, как их запускать, останавливать и уничтожать. Я буду использовать unit-тестирование, что позволит все мини-примеры превращать в автоматизированные тесты вашего проекта.
В завершении могу сказать, что данный класс успешно работает и проверен мной лично на Windows (32- и 64-бит), Linux 2.6 (32- и 64-бит Intel и SPARC), AIX 5.3 и 6, SunOS 5.2 64-bit SPARC, HP-UX и HP-UX IA64.
Процессы и потоки in-depth. Обзор различных потоковых моделей
Здравствуйте дорогие читатели. В данной статье мы рассмотрим различные потоковые модели, которые реализованы в современных ОС (preemptive, cooperative threads). Также кратко рассмотрим как потоки и средства синхронизации реализованы в Win32 API и Posix Threads. Хотя на Хабре больше популярны скриптовые языки, однако основы — должны знать все 😉
Потоки, процессы, контексты.
Системный вызов (syscall). Данное понятие, вы будете встречать достаточно часто в данной статье, однако несмотря на всю мощь звучания, его определение достаточно простое 🙂 Системный вызов — это процесс вызова функции ядра, из приложение пользователя. Режим ядра — код, который выполняется в нулевом кольце защиты процессора (ring0) с максимальными привилегиями. Режим пользователя — код, исполняемый в третьем кольце защиты процессора (ring3), обладает пониженными привилегиями. Если код в ring3 будет использовать одну из запрещенных инструкций (к примеру rdmsr/wrmsr, in/out, попытку чтения регистра cr3, cr4 и т.д.), сработает аппаратное исключение и пользовательский процесс, чей код исполнял процессор в большинстве случаях будет прерван. Системный вызов осуществляет переход из режима ядра в режим пользователя с помощью вызова инструкции syscall/sysenter, int2eh в Win2k, int80h в Linux и т.д.
И так, что же такое поток? Поток (thread) — это, сущность операционной системы, процесс выполнения на процессоре набора инструкций, точнее говоря программного кода. Общее назначение потоков — параллельное выполнение на процессоре двух или более различных задач. Как можно догадаться, потоки были первым шагом на пути к многозадачным ОС. Планировщик ОС, руководствуясь приоритетом потока, распределяет кванты времени между разными потоками и ставит потоки на выполнение.
На ряду с потоком, существует также такая сущность, как процесс. Процесс (process) — не что более иное, как некая абстракция, которая инкапсулирует в себе все ресурсы процесса (открытые файлы, файлы отображенные в память. ) и их дескрипторы, потоки и т.д. Каждый процесс имеет как минимум один поток. Также каждый процесс имеет свое собственное виртуальное адресное пространство и контекст выполнения, а потоки одного процесса разделяют адресное пространство процесса.
Классификация потоков
Классификация потоков по отображению в режим ядра
Модель N:M отображает некоторое число потоков пользовательских процессов N на M потоков режима ядра. Проще говоря имеем некую гибридную систему, когда часть потоков ставится на выполнение в планировщике ОС, а большая их часть в планировщике потоков процесса или библиотеки потоков. Как пример можно привести GNU Portable Threads. Данная модель достаточно трудно реализуема, но обладает большей производительностью, так как можно избежать значительного количества системных вызовов.
Модель N:1. Как вы наверное догадались — множество потоков пользовательского процесса отображаются на один поток ядра ОС. Например волокна.
Классификация потоков по многозадачной модели
Однако, кооперативная многозадачность со временем показала свою несостоятельность. Росли объемы данных хранимых на винчестерах, росла также скорость передачи данных в сетях. Стало понятно, что некоторые потоки должны иметь больший приоритет, как-то потоки обслуживания прерываний устройств, обработки синхронных IO операций и т.д. В это время каждый поток и процесс в системе обзавелся таким свойством, как приоритет. Подробнее о приоритетах потоков и процессов в Win32 API вы можете прочесть в книге Джефри Рихтера, мы на этом останавливатся не будем 😉 Таким образом поток с большим приоритетом, может вытеснить поток с меньшим. Такой прицип лег в основу вытесняющей многозадачности (preemptive multitasking). Сейчас все современные ОС используют данный подход, за исключением реализации волокон в пользовательском режиме.
Классификация потоков по уровню реализации
Win32 API Threads
Если вы все еще не устали, предлагаю небольшой обзор API для работы с потоками и средствами синхронизации в win32 API. Если вы уже знакомы с материалом, можете смело пропускать этот раздел 😉
Потоки в Win32 создаются с помощью функции CreateThread, куда передается указатель на функцию (назовем ее функцией потока), которая будет выполнятся в созданом потоке. Поток считается завершенным, когда выполнится функция потока. Если же вы хотите гарантировать, что поток завершен, то можно воспользоватся функцией TerminateThread, однако не злоупотребляйте ею! Данная функция «убивает» поток, и отнюдь не всегда делает это корректно. Функция ExitThread будет вызвана неявно, когда завершится функция потока, или же вы можете вызвать данную функцию самостоятельно. Главная ее задача — освободить стек потока и его хендл, т.е. структуры ядра, которые обслуживают данный поток.
Поток в Win32 может пребывать в состоянии сна (suspend). Можно «усыпить поток» с помощью вызова функции SuspendThread, и «разбудить» его с помощью вызова ResumeThread, также поток можно перевести в состояние сна при создании, установив значение параметра СreateSuspended функции CreateThread. Не стоит удивлятся, если вы не увидите подобной функциональности в кроссплатформенных библиотеках, типа boost::threads и QT. Все очень просто, pthreads просто не поддерживают подобную функциональность.
Средства синхронихации в Win32 есть двух типов: реализованные на уровне пользователя, и на уровне ядра. Первые — это критические секции (critical section), к второму набору относят мьютексы (mutex), события (event) и семафоры (semaphore).
Критические секции — легковесный механизм синхронизации, который работает на уровне пользовательского процесса и не использует тяжелых системных вызовов. Он основан на механизме взаимных блокировок или спин локов (spin lock). Поток, который желает обезопасить определенные данные от race conditions вызывает функцию EnterCliticalSection/TryEnterCriticalSection. Если критическая секция свободна — поток занимает ее, если же нет — поток блокируется (т.е. не выполняется и не отъедает процессорное время) до тех пор, пока секция не будет освобождена другим потоком с помощью вызова функции LeaveCriticalSection. Данные функции — атомарные, т.е. вы можете не переживать за целостность ваших данных 😉
Posix Threads или pthreads
Сложно представить, какая из *nix подобных операционных систем, не реализует этот стандарт. Стоит отметить, что pthreads также используется в различных операционных системах реального времени (RTOS), потому требование к этой библиотеке (вернее стандарту) — жестче. К примеру, поток pthread не может пребывать в состоянии сна. Также в pthread нет событий, но есть гораздо более мощный механизм — условных переменных (conditional variables), который с лихвой покрывает все необходимые нужды.
Поговорим об отличиях. К примеру, поток в pthreads может быть отменен (cancel), т.е. просто снят с выполнения посредством системного вызова pthread_cancel в момент ожидания освобождения какого-нибудь мьютекса или условной переменной, в момент выполнения вызова pthread_join (вызывающий поток блокируется до тех пор, пока не закончит свое выполнение поток, приминительно к которому была вызвана функция) и т.д. Для работы с мьютексами и семафорами существует отдельные вызовы, как-то pthread_mutex_lock/pthread_mutex_unlock и т.д.
Conditional variables (cv) обычно используется в паре с мьютексами в более сложных случаях. Если мьютекс просто блокирует поток, до тех пор, пока другой поток не освободит его, то cv создают условия, когда поток может заблокировать сам себя до тех пор, пока не произойдет какое-либо условия разблокировки. Например, механизм cv помогает эмулировать события в среде pthreads. Итак, системный вызов pthread_cond_wait ждет, пока поток не будет уведомлен о том, что случилось определенное событие. pthread_cond_signal уведомляет один поток из очереди, что cv сработала. pthread_cond_broadcast уведомляет все потоки, которые вызывали pthread_cond_wait, что сработала cv.
Прощальное слово
На сегодня пожалуй все, иначе информации станет слишком много. Для интересующихся, есть несколько полезных ссылок и книг внизу 😉 Также высказывайте свое мнение, интересны ли вам статьи по данной теме.
UPD: дополнил статью небольшой информацией о режиме ядра и режиме пользователя.
UPD2: исправил досадные промахи и ошибки. Спасибо комментаторам 😉