mingw-w64 threads: posix vs win32
GCC comes with a compiler runtime library (libgcc) which it uses for (among other things) providing a low-level OS abstraction for multithreading related functionality in the languages it supports. The most relevant example is libstdc++’s C++11 <thread> , <mutex> , and <future> , which do not have a complete implementation when GCC is built with its internal Win32 threading model. MinGW-w64 provides a winpthreads (a pthreads implementation on top of the Win32 multithreading API) which GCC can then link in to enable all the fancy features.
I must stress this option does not forbid you to write any code you want (it has absolutely NO influence on what API you can call in your code). It only reflects what GCC’s runtime libraries (libgcc/libstdc++/. ) use for their functionality. The caveat quoted by @James has nothing to do with GCC’s internal threading model, but rather with Microsoft’s CRT implementation.
- posix : enable C++11/C11 multithreading features. Makes libgcc depend on libwinpthreads, so that even if you don’t directly call pthreads API, you’ll be distributing the winpthreads DLL. There’s nothing wrong with distributing one more DLL with your application.
- win32 : No C++11 multithreading features.
Neither have influence on any user code calling Win32 APIs or pthreads APIs. You can always use both.
Solution 2
Parts of the GCC runtime (the exception handling, in particular) are dependent on the threading model being used. So, if you’re using the version of the runtime that was built with POSIX threads, but decide to create threads in your own code with the Win32 APIs, you’re likely to have problems at some point.
Even if you’re using the Win32 threading version of the runtime you probably shouldn’t be calling the Win32 APIs directly. Quoting from the MinGW FAQ:
As MinGW uses the standard Microsoft C runtime library which comes with Windows, you should be careful and use the correct function to generate a new thread. In particular, the CreateThread function will not setup the stack correctly for the C runtime library. You should use _beginthreadex instead, which is (almost) completely compatible with CreateThread .
Solution 3
Note that it is now possible to use some of C++11 std::thread in the win32 threading mode. These header-only adapters worked out of the box for me: https://github.com/meganz/mingw-std-threads
From the revision history it looks like there is some recent attempt to make this a part of the mingw64 runtime.
Solution 4
@rubenvb answer is fully correct, use the mingw posix compiler if you want to use std::thread , std::mutex , etc. For everybody who is using CMake, here is an example:
mingw-w64 threads: posix vs win32
I’m installing mingw-w64 on Windows and there are two options: win32 threads and posix threads. I know what is the difference between win32 threads and pthreads but I don’t understand what is the difference between these two options. I doubt that if I will choose posix threads it will prevent me from calling WinAPI functions like CreateThread.
It seems that this option specify which threading API will be used by some program or library, but by what? By GCC, libstdc++ or by something else?
In short, for this version of mingw, the threads-posix release will use the posix API and allow the use of std::thread, and the threads-win32 will use the win32 API, and disable the std::thread part of the standard.
Ok, if I will select win32 threads then std::thread will be unavailable but win32 threads will still be used. But used by what?
4 Answers 4
GCC comes with a compiler runtime library (libgcc) which it uses for (among other things) providing a low-level OS abstraction for multithreading related functionality in the languages it supports. The most relevant example is libstdc++’s C++11 <thread> , <mutex> , and <future> , which do not have a complete implementation when GCC is built with its internal Win32 threading model. MinGW-w64 provides a winpthreads (a pthreads implementation on top of the Win32 multithreading API) which GCC can then link in to enable all the fancy features.
I must stress this option does not forbid you to write any code you want (it has absolutely NO influence on what API you can call in your code). It only reflects what GCC’s runtime libraries (libgcc/libstdc++/. ) use for their functionality. The caveat quoted by @James has nothing to do with GCC’s internal threading model, but rather with Microsoft’s CRT implementation.
- posix : enable C++11/C11 multithreading features. Makes libgcc depend on libwinpthreads, so that even if you don’t directly call pthreads API, you’ll be distributing the winpthreads DLL. There’s nothing wrong with distributing one more DLL with your application.
- win32 : No C++11 multithreading features.
Neither have influence on any user code calling Win32 APIs or pthreads APIs. You can always use both.
POSIX Threads for Windows
An implementation of the POSIX threads API for Windows
- Summary
- Files
- Reviews
- Support
- Wiki
- Code
Also known as "pthreads-win32", POSIX Threads for Windows implements a large subset of the threads related API from the Single Unix Specification Version 3.
Conformance and quality are high priorities of this mature library. Development began in 1998 and has continued with numerous significant professional contributions.
Please note:- whilst PThreads4W can be built and run by it, MinGW64 includes it's own default POSIX Threads library called "winpthreads". The two are not compatible and in order to build and run PThreads4W (formerly PThreads-WIn32) MinGW64 must be installed without win32pthreads. If you want or need to build and run with PThreads4W then you need to choose win32 threading instead of POSIX when you install MinGW64 to not install the conflicting winpthreads include and library files.
Threads posix win32 что это
Процессы и потоки 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) — не что более иное, как некая абстракция, которая инкапсулирует в себе все ресурсы процесса (открытые файлы, файлы отображенные в память. ) и их дескрипторы, потоки и т.д. Каждый процесс имеет как минимум один поток. Также каждый процесс имеет свое собственное виртуальное адресное пространство и контекст выполнения, а потоки одного процесса разделяют адресное пространство процесса.
- Регистры процессора.
- Указатель на стек потока/процесса.
- Если ваша задача требует интенсивного распараллеливания, используйте потоки одного процесса, вместо нескольких процессов. Все потому, что переключение контекста процесса происходит гораздо медленнее, чем контекста потока.
- При использовании потока, старайтесь не злоупотреблять средствами синхронизации, которые требуют системных вызовов ядра (например мьютексы). Переключение в редим ядра — дорогостоящая операция!
- Если вы пишете код, исполняемый в ring0 (к примеру драйвер), старайтесь обойтись без использования дополнительных потоков, так как смена контекста потока — дорогостоящая операция.
Классификация потоков
- По отображению в ядро: 1:1, N:M, N:1
- По многозадачной модели: вытесняющая многозадачность (preemptive multitasking), кооперативная многозадачность (cooperative multitasking).
- По уровню реализации: режим ядра, режим польователя, гибридная реализация.
Классификация потоков по отображению в режим ядра
- Центральный планировщик ОС режима ядра, который распределяет время между любым потоком в системе.
- Планировщик библиотеки потоков. У библиотеки потоков режима пользователя может быть свой планировщик, который распределяет время между потоками различных процессов режима пользователя.
- Планировщик потоков процесса. Уже рассмотренные нами волокна, ставятся на выполнение именно таким способом. К примеру свой Thread Manager есть у каждого процесса Mac OS X, написанного с использованием библиотеки Carbon.
Модель N:M отображает некоторое число потоков пользовательских процессов N на M потоков режима ядра. Проще говоря имеем некую гибридную систему, когда часть потоков ставится на выполнение в планировщике ОС, а большая их часть в планировщике потоков процесса или библиотеки потоков. Как пример можно привести GNU Portable Threads. Данная модель достаточно трудно реализуема, но обладает большей производительностью, так как можно избежать значительного количества системных вызовов.
Модель N:1. Как вы наверное догадались — множество потоков пользовательского процесса отображаются на один поток ядра ОС. Например волокна.
Классификация потоков по многозадачной модели
Во времена DOS, когда однозадачные ОС перестали удовлетворять потребителя, программисты и архитекторы задумали реализовать многозадачную ОС. Самое простое решение было следующим: взять общее количество потоков, определить какой-нибудь минимальный интервал выполнения одного потока, да взять и разделить между всеми -братьями- потоками время выполнения поровну. Так и появилось понятие кооперативной многозадачности (cooperative multitasking), т.е. все потоки выполняются поочередно, с равным временем выполнения. Никакой другой поток, не может вытеснить текущий выполняющийся поток. Такой очень простой и очевидный подход нашел свое применение во всех версиях Mac OS вплоть до Mac OS X, также в Windows до Windows 95, и Windows NT. До сих пор кооперативная многозадачность используется в Win32 для запуска 16 битных приложений. Также для обеспечения совместимости, cooperative multitasking используется менеджером потоков в Carbon приложениях для Mac OS X.
Однако, кооперативная многозадачность со временем показала свою несостоятельность. Росли объемы данных хранимых на винчестерах, росла также скорость передачи данных в сетях. Стало понятно, что некоторые потоки должны иметь больший приоритет, как-то потоки обслуживания прерываний устройств, обработки синхронных IO операций и т.д. В это время каждый поток и процесс в системе обзавелся таким свойством, как приоритет. Подробнее о приоритетах потоков и процессов в Win32 API вы можете прочесть в книге Джефри Рихтера, мы на этом останавливатся не будем Таким образом поток с большим приоритетом, может вытеснить поток с меньшим. Такой прицип лег в основу вытесняющей многозадачности (preemptive multitasking). Сейчас все современные ОС используют данный подход, за исключением реализации волокон в пользовательском режиме.
Классификация потоков по уровню реализации
- Реализация потоков на уровне ядра. Проще говоря, это классическая 1:1 модель. Под эту категорию подпадают:
- Потоки Win32.
- Реализация Posix Threads в Linux — Native Posix Threads Library (NPTL). Дело в том, что до версии ядра 2.6 pthreads в Linux был целиком и полностью реализован в режиме пользователя (LinuxThreads). LinuxThreads реализовывалf модель 1:1 следующим образом: при создании нового потока, библиотека осуществляла системный вызов clone, и создавало новый процесс, который тем не менее разделял единое адресное пространство с родительским. Это породило множество проблем, к примеру потоки имели разные идентификаторы процесса, что противоречило некоторым аспектам стандарта Posix, которые касаются планировщика, сигналов, примитивов синхронизации. Также модель вытеснения потоков, работала во многих случаях с ошибками, по этому поддержку pthread решено было положить на плечи ядра. Сразу две разработки велись в данном направлении компаниями IBM и Red Hat. Однако, реализация IBM не снискала должной популярности, и не была включена ни в один из дистрибутивов, потому IBM приостановила дальнейшую разработку и поддержку библиотеки (NGPT). Позднее NPTL вошли в библиотеку glibc.
- Легковесные ядерны потоки (Leight Weight Kernel Threads — LWKT), например в DragonFlyBSD. Отличие этих потоков, от других потоков режима ядра в том, что легковесные ядерные потоки могут вытеснять другие ядерные потоки. В DragonFlyBSD существует множество ядерных потоков, например поток обслуживания аппаратных прерываний, поток обслуживания программных прерываний и т.д. Все они работают с фиксированным приоритетом, так вот LWKT могут вытеснять эти потоки (preempt). Конечно это уже более специфические вещи, про которые можно говорить бесконечно, но приведу еще два примера. В Windows все потоки ядра выполняются либо в контексте потока инициировавшего системный вызов/IO операцию, либо в контексте потока системного процесса system. В Mac OS X существует еще более интересная система. В ядре есть лишь понятие task, т.е. задачи. Все операции ядра выполняются в контексте kernel_task. Обработка аппаратного прерывания, к примеру, происходит в контексте потока драйвера, который обслуживает данное прерывание.
- Реализация потоков в пользовательском режиме. Так как, системный вызов и смена контекста — достаточно тяжелые операции, идея реализовать поддержку потоков в режиме пользователя витает в воздухе давно. Множество попыток было сделано, однако данная методика популярности не обрела:
- GNU Portable Threads — реализация Posix Threads в пользовательском режиме. Основное преимущество — высокая портабельность данной библиотеки, проще говоря она может быть легко перенесена на другие ОС. Проблему вытиснения потоков в данной библиотеке решили очень просто — потоки в ней не вытесняются Ну и конечно ни о какой мультмпроцессорности речь идти не может. Данная библиотека реализует модель N:1.
- Carbon Threads, которые я упоминал уже не раз, и RealBasic Threads.
- Гибридная реализация. Попытка использовать все преимущества первого и второго подхода, но как правило подобные мутанты обладают гораздо бОльшими недостатками, нежели достоинствами. Один из примеров: реализация Posix Threads в NetBSD по модели N:M, которая была посже заменена на систему 1:1. Более подробно вы можете прочесть в публикации Scheduler Activations: Effective Kernel Support for the User-Level Management of Parallelism.
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. Данные функции — атомарные, т.е. вы можете не переживать за целостность ваших данных
- Они использует примитивы ядра при выполнении, т.е. системные вызовы, что сказывается не производительности.
- Могут быть именованными и не именованными, т.е. каждому такому объекту синхронизации можно присвоить имя.
- Работают на уровне системы, а не на уровне процесса, т.е. могут служить механизмом межпроцессного взаимодействия (IPC).
- Используют для ожидания и захвата примитива единую функцию: WaitForSingleObject/WaitForMultipleObjects.
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: исправил досадные промахи и ошибки. Спасибо комментаторам
posix win threads for windows что это
Современные операционные системы и микропроцессоры уже давно поддерживает многозадачность и вместе с тем, каждая из этих задач может выполняться в несколько потоков. Это дает ощутимый прирост производительности вычислений и позволяет лучше масштабировать пользовательские приложения и сервера, но за это приходится платить цену — усложняется разработка программы и ее отладка.
В этой статье мы познакомимся с POSIX Threads для того, чтобы затем узнать как это все работает в Linux. Не заходя в дебри синхронизации и сигналов, рассмотрим основные элементы Pthreads. Итак, под капотом потоки.
Общие сведения
У всех исполняемых процессов есть как минимум один поток исполнения. Некоторые процессы этим и ограничиваются в тех случаях, когда дополнительные нити исполнения не дают прироста производительности, но только усложняют программу. Однако таких программ с каждым днем становится относительно меньше.
В чем польза множественных потоков исполнения? Возьмем какой-нибудь загруженный веб сервер, например habrahabr.ru. Если бы сервер создавал отдельный процесс для обслуживания каждого http запроса, мы бы ожидали вечно пока загрузится наша страница. Создания нового процесса — дорогостоящее удовольствие для ОС. Даже учитывая оптимизацию за счет копирования при записи, системные вызовы fork и exec создают новые копии страниц памяти и списка файловых описателей. В целом ядро ОС может создать новый поток на порядок быстрее, чем новый процесс.
Таблицы страниц до и после изменения общей страницы памяти во время копирования при записи.
Существует закономерность между количеством параллельных нитей исполнения процесса, алгоритмом программы и ростом производительности. Это зависимость называется Законом Амдаля.
Закон Амдаля для распараллеливания процессов.
Используя уравнение, показанное на рисунке, можно вычислить максимальное улучшение производительности системы, использующей N процессоров и фактор F, который указывает, какая часть системы не может быть распараллелена. Например 75% кода запускается параллельно, а 25% — последовательно. В таком случае на двухядерном процессоре будет достигнуто 1.6 кратное ускорение программы, на четырехядерном процессоре — 2.28571 кратное, а предельное значение ускорения при N стремящемся к бесконечности равно 4.
Отображение потоков в режим ядра
Практически все современные ОС — включая Windows, Linux, Mac OS X, и Solaris — поддерживают управление потоками в режиме ядра. Однако потоки могут быть созданы не только в режиме ядра, но и в режиме пользователя. При использовании этого уровня ядро не знает о существовании потоков — все управление потоками реализуется приложением с помощью специальных библиотек. Пользовательские потоки по разному отображаются на потоки в режиме ядра. Всего существует три модели, из которых 1:1 является наиболее часто используемой.
Отображение N:1
В данной модели несколько пользовательских потоков отображаются на один поток ядра ОС. Все управление потоками осуществляет особая пользовательская библиотека, и в этом преимущество такого подхода. Недостаток же в том, что если один единственный поток выполняет блокирующий вызов, то тогда тормозится весь процесс. Предыдущие версии Solaris OS использовали такую модель, но затем вынуждены были от нее отказаться.
Отображение 1:1
Это самая проста модель, в которой каждый поток созданный в каком-нибудь процессе непосредственно управляется планировщиком ядра ОС и отображается на один единственный поток в режиме ядра. Чтобы приложение не плодило бесконтрольно потоки, перегружая ОС, вводят ограничение на максимальное количество потоков поддерживаемых в ОС. Данный способ отображения потоков поддерживают ОС Linux и Windows.
Отображение M:N
При таком подходе M пользовательских потоков мультиплексируются в такое же или меньшее N количество потоков ядра. Преодолеваются негативные эффекты двух других моделей: нити по-настоящему исполняются параллельно и нет необходимости в ОС вводить ограничения на их общее количество. Вместе с тем данную модель довольно трудно реализовать с точки зрения программирования.
Потоки POSIX
В конце 1980-х и начале 1990-х было несколько разных API, но в 1995 г. POSIX.1c стандартизовал потоки POSIX, позже это стало частью спецификаций SUSv3. В наше время многоядерные процессоры проникли даже в настольные ПК и смартфоны, так что у большинства машин есть низкоуровневая аппаратная поддержка, позволяющая им одновременно выполнять несколько потоков. В былые времена одновременное исполнение потоков на одноядерных ЦПУ было лишь впечатляюще изобретательной, но очень эффективной иллюзией.
Pthreads определяет набор типов и функций на Си.
Из man errno
Переменная errno определена в стандарте ISO C как изменяемое lvalue int и не объявляемая явно; errno может быть и макросом. Переменная errno является локальным значением нити; её изменение в одной нити не влияет на её значение в другой нити.
Создание потока
При удачном завершении pthread_create() возвращает код 0, ненулевое значение сигнализирует об ошибке.
Рассмотрим теперь пример многопоточной программы.
Завершение потока
Поток завершает выполнение задачи когда:
Синтаксис проще, чем при создании потока.
Ожидание потока
При удачном завершении pthread_join() возвращает код 0, ненулевое значение сигнализирует об ошибке.
Досрочное завершение потока
При удачном завершении pthread_cancel() возвращает код 0, ненулевое значение сигнализирует об ошибке.
Небольшая иллюстрация создания и отмены потока.
Чтобы не создалось впечатление, что тут царит произвол и непредсказуемость результатов данного вызова, рассмотрим таблицу параметров, которые определяют поведение потока после получения вызова на досрочное завершение.
Отсоединение потока
При удачном завершении pthread_detach() возвращает код 0, ненулевое значение сигнализирует об ошибке.
Потоки versus процессы
Напоследок предлагаю рассмотреть несколько соображений на тему, следует ли проектировать приложение многопоточным или запускать его в несколько процессов с одним потоком? Сперва выгоды параллельных множественных потоков.
В начальной части статьи мы уже указывали на эти преимущество, поэтому вкратце их просто перечислим.
Теперь немного о недостатках.
Тема потоков практически бездонна, даже основы работы с потоками может потянуть на пару лекций, но мы уже знаем достаточно, чтобы изучить структуру многопоточных приложений в Linux.
Национальная библиотека им. Н. Э. Баумана
Bauman National Library
Персональные инструменты
POSIX Threads
Содержание
Причины использования потоков
В традиционных операционных системах у каждого процесса есть адресное пространство и единственный поток управления. Фактически это почти что определение процесса. Тем не менее нередко возникают ситуации, когда неплохо было бы иметь в одном и том же адресном пространстве несколько потоков управления, выполняемых квазипараллельно, как будто они являются чуть ли не обособленными процессами (за исключением общего адресного пространства).
Основная причина использования потоков заключается в том, что во многих приложениях одновременно происходит несколько действий, часть которых может периодически быть заблокированной. Модель программирования упрощается за счет разделения такого приложения на несколько последовательных потоков, выполняемых в квазипараллельном режиме. Рассматривая потоки, мы добавляем новый элемент: возможность использования параллельными процессами единого адресного пространства и всех имеющихся данных. Эта возможность играет весьма важную роль для тех приложений, которым не подходит использование нескольких процессов (с их раздельными адресными пространствами).
Вторым аргументом в пользу потоков является легкость (то есть быстрота) их создания и ликвидации по сравнению с более «тяжеловесными» процессами. Во многих системах создание потоков осуществляется в 10–100 раз быстрее, чем создание процессов. Это свойство особенно пригодится, когда потребуется быстро и динамично изменять количество потоков. [Источник 1]
Третий аргумент в пользу потоков также касается производительности. Когда потоки работают в рамках одного центрального процессора, они не приносят никакого прироста производительности, но когда выполняются значительные вычисления, а также значительная часть времени тратится на ожидание ввода-вывода, наличие потоков позволяет этим действиям перекрываться по времени, ускоряя работу приложения.
И наконец, потоки весьма полезны для систем, имеющих несколько центральных процессоров, где есть реальная возможность параллельных вычислений.
Основные функции
В стандарте определено более 60 вызовов функций. Они могут быть разделены на 4 категории:
Опишем ряд самых основных функций, чтобы дать представление о том, как они работают.
выполняться другому потоку
Два следующих вызова функций, связанных с потоками, относятся к атрибутам. Функция 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 используются, чтобы продолжать вычисления во время ожидания медленных или блокирующих операций, для проектирования модульных программ, явно выражая независимость тех или иных событий в программе, для эффективного использования параллелизма на многопроцессорных системах.
Реализации POSIX Threads API
Так и живем, не пропустив ни дня,
И каждый день проходит словно дважды.
Стандарт POSIX допускает различные подходы к реализации многопоточности в рамках одного процесса. Возможны три основных подхода:
Как правило, при этом количество пользовательских нитей в процессе может превосходить количество системных нитей.
1. Пользовательские нити
Реализация планировщика в пользовательском адресном пространстве не представляет больших сложностей; наброски реализаций таких планировщиков приводятся во многих учебниках по операционным системам, в том числе в [Иртегов 2002]. Учебная многозадачная ОС Minix может быть собрана и запущена в виде задачи под обычной Unix-системой; при этом процессы Minix будут с точки зрения ядра системы-хозяина пользовательскими нитями. Главным достоинством пользовательского планировщика считается тот факт, что он может быть реализован без изменений ядра системы.
2. Системные нити
3. Гибридная реализация
Причина, по которой этот подход нашел практическое применение – это убеждение разработчиков первых многопоточных версий Unix, что пользовательские нити дешевле системных, требуют меньше ресурсов для своего исполнения.
Таким образом, если все пользовательские нити исполняли блокирующиеся системные вызовы, то количество LWP могло сравняться с количеством пользовательских нитей. Можно предположить, что компания Sun отказалась от гибридной реализации многопоточности именно потому, что обнаружилось, что такое происходит со многими реальными прикладными программами.
4. Сборка приложений с POSIX Threads
Ниже приводится пример кода, который позволяет протестировать ваш компилятор и проверить наличие типичных препроцессорных символов, используемых в include-файлах стандартной библиотеки языка С для проверки того, однопоточная или многопоточная программа сейчас компилируется. _LIBC_REENTRANT используется в Linux, _REENTRANT в Solaris. На других платформах могут использоваться другие символы. Попробуйте собрать эту программу с разными ключами компилятора и проверить результат. Полезно также поискать соответствующие символы в файлах каталога /usr/include и посмотреть, какие именно конструкции они контролируют.
5. Программы для экспериментов
темы 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.
Глава 12
В главе 11 вы видели, как обрабатываются процессы в ОС Linux (и конечно в UNIX). Эти средства обработки множественных процессов долгое время были характерной чертой UNIX-подобных операционных систем. Порой бывает полезно заставить одну программу делать два дела одновременно или, по крайней мере, создать впечатление такой работы. А может быть, вы хотите, чтобы несколько событий произошло одновременно и все они были тесно связаны, но при этом накладные расходы на создание нового процесса с помощью функции
В этой главе мы рассмотрим следующие темы:
□ создание новых потоков в процессе;
□ синхронизацию доступа к данным потоков одного процесса;
□ изменение атрибутов потока;
□ управление в одном и том же процессе одним потоком из другого.
Что такое поток?
Множественные нити исполнения в одной программе называют потоками. Более точно поток — это последовательность или цикл управления в процессе. Все программы, которые вы видели до настоящего момента, выполняли единственный процесс, хотя, как и многие другие операционные системы, ОС Linux вполне способна выполнять множественные процессы одновременно. В действительности у всех процессов есть как минимум один поток исполнения. У всех процессов, с которыми вы пока познакомились в этой книге, был только один поток исполнения.
Важно понять разницу между системным вызовом fork и созданием новых потоков. Когда процесс выполняет системный вызов fork, создается новая копия процесса с ее собственными переменными и собственным PID. Время выполнения этого нового процесса планируется независимо и выполняется он (в основном) независимо от создавшего его процесса. Когда мы создаем в процессе новый поток, этот поток исполнения в противоположность новому процессу получает собственный стек (и, следовательно, локальные переменные), но использует совместно с создавшим его процессом глобальные переменные, файловые дескрипторы, обработчики сигналов и положение текущего каталога.
Идея потоков была популярна какое-то время, но пока Комитет IEEE POSIX не опубликовал некоторые стандарты, потоки не были широко распространены в UNIX-подобных операционных системах и существовавшие реализации разных поставщиков сильно отличались друг от друга. С появлением стандарта POSIX 1003.1c все изменилось; потоки теперь не только лучше стандартизованы, но также реализованы в большинстве дистрибутивов Linux. В наше время многоядерные процессоры стали обычными даже в настольных компьютерах, так что у большинства машин есть низкоуровневая аппаратная поддержка, позволяющая им выполнять несколько потоков одновременно. Раньше при наличии одноядерных ЦПУ одновременное исполнение потоков было лишь изобретательной, хотя и очень эффективной иллюзией.
Впервые ОС Linux обзавелась поддержкой потоков около 1996 г. благодаря появлению библиотеки, которую часто называют «LinuxThreads» (потоки Linux). Она почти соответствует стандарту POSIX (на самом деле в большинстве случаев отличия не заметны) и стала важным шагом на пути первого применения потоков программистами Linux. Но между реализацией потоков в Linux и стандартом POSIX есть слабые расхождения, в основном касающиеся обработки сигналов. Ограничения накладываются не столько реализацией библиотеки, сколько низкоуровневой поддержкой ядра Linux.
Разные проекты рассматривали возможности улучшения поддержки потоков в Linux, касающиеся не только устранения слабых расхождений со стандартом POSIX, но и повышения производительности и удаления любых ненужных ограничений. Основная работа была направлена на поиск способов отображения потоков пользовательского уровня на потоки уровня ядра системы. Двумя главными проектами были New Generation POSIX Threads (NGPT, потоки POSIX нового поколения) и Native POSIX Thread Library (NPTL, библиотека истинных потоков POSIX). Оба проекта должны были внести изменения в ядро Linux, обеспечивающие поддержку новых библиотек, и оба предлагали существенное повышение производительности по сравнению с прежней реализацией потоков в Linux.
В 2002 г. команда NGPT объявила, что не хочет разделять сообщество и приостанавливает разработку новых средств для проекта NGPT, но продолжит работу по улучшению поддержки потоков в ОС Linux, присоединив свои усилия к стараниям NPTL. Библиотека NPTL стала новым стандартом для потоков в Linux, выпустив первую основную версию в дистрибутиве Red Hat Linux 9. Вы можете найти интересную основополагающую информацию о NPTL в статье «The Native POSIX Thread Library for Linux» («Библиотека истинных потоков POSIX для Linux») Ульриха Дреппера (Ulrich Drepper) и Инго Мольнара (Ingo Molnar), которая во время написания книги была доступна в Интернете по адресу http://people.redhat.com/drepper/nptl-design.pdf.
Большая часть программного кода из этой главы будет работать с любой библиотекой потоков, поскольку основана на стандарте POSIX, общем для всех библиотек потоков. Но вы сможете заметить небольшие отличия, если пользуетесь старой версией дистрибутива Linux, особенно когда примените команду
Достоинства и недостатки потоков
В определенных обстоятельствах создание нового потока обладает явно выраженными преимуществами по сравнению с созданием нового процесса. Накладные расходы при создании нового потока существенно меньше, чем при создании нового процесса (несмотря на то, что создание новых процессов в Linux очень эффективно по сравнению с другими операционными системами).
Далее перечислены некоторые достоинства потоков.
□ Иногда очень полезно создать программу, которая выполняет два дела одновременно. Классический пример — подсчет в режиме реального времени слов в документе в ходе редактирования текста. Один поток может управлять пользовательским вводом и выполнять редактирование. Другой, способный видеть то же содержимое документа, может непрерывно обновлять переменную-счетчик количества слов. Первый поток (или даже третий) может использовать эту переменную для информирования пользователя. Другой пример — многопоточный сервер базы данных, в котором единый наблюдаемый процесс обслуживает множество клиентов, улучшая общую пропускную способность за счет обслуживания одних запросов и одновременной блокировки других, ожидающих готовности диска. Серверу базы данных реализовать эту скрытую многозадачность в разных процессах очень трудно, т.к. требования блокировки и непротиворечивости данных приводят к тесной связи двух этих процессов. С помощью множественных потоков воплотить в жизнь этот алгоритм гораздо легче.
□ Производительность приложения, в котором смешаны ввод, вычисления и вывод, можно повысить, запустив эти операции как три отдельных потока. Пока поток ввода или вывода ждет подсоединения, один из оставшихся потоков может продолжить вычисления. Серверное приложение, обрабатывающее многочисленные сетевые подключения, также может подойти для организации программы с множественными потоками.
□ Сейчас, когда многоядерные ЦПУ обычны в настольных и портативных компьютерах, применение множественных потоков внутри процесса может при наличии подходящего приложения позволить одному процессу лучше использовать доступные аппаратные ресурсы.
□ Вообще переключение между потоками требует от операционной системы гораздо меньше усилий, чем переключение между процессами. Таким образом, множественные потоки гораздо менее требовательны к ресурсам, чем множественные процессы, и с ними гораздо практичнее выполнять в однопроцессорных системах программы, логика которых требует применения нескольких потоков исполнения. Считается, что трудности разработки при написании многопоточной программы весьма значительны, и это утверждение нельзя не принимать всерьез.
У потоков есть и недостатки.
□ Создание многопоточной программы требует очень тщательной разработки. Вероятность появления незначительных временных сбоев или ошибок, вызванных нечаянным совместным использованием переменных, в такой программе весьма значительна. Алан Кокс (Alan Сох, всеми уважаемый гуру Linux) сказал, что потоки равнозначны умению «выстрелить в обе собственные ноги одновременно».
□ Отладка многопоточной программы гораздо труднее, чем отладка одного потока исполнения, поскольку взаимосвязи потоков очень трудно контролировать.
□ Программа, в которой громоздкие вычисления разделены на две части, и эти две части выполняются как отдельные потоки, необязательно будет работать быстрее на машине с одним процессором, если только вычисление не позволяет выполнять обе ее части одновременно и у машины, на которой выполняется программа, нет многоядерного процессора для поддержки истинной многопоточности.
Первая программа с применением потоков
Существует целый ряд библиотечных вызовов, связанных с потоками, большинство имен которых начинается с префикса pthread. Для применения этих библиотечных вызовов вы должны определить макрос
Когда разрабатывались первые версии библиотечных подпрограмм UNIX и POSIX, предполагалось, что в каждом процессе будет только один поток исполнения. Яркий пример — переменная
Вам нужны реентерабельные подпрограммы. Реентерабельный программный код может вызываться несколько раз либо разными потоками, либо каким-то образом вложенными вызовами и при этом работать корректно. Следовательно, реентерабельная часть программного кода обычно должна применять локальные переменные таким образом, чтобы любой и каждый вызов кода получал собственную уникальную копию данных.
В многопоточных программах вы сообщаете компилятору, что вам нужно это средство, определяя в вашей программе макрос
□ Некоторые функции получают безопасный реентерабельный вариант прототипа или объявления. При этом имя функции остается обычно прежним, но в конце добавляется суффикс
□ Некоторые функции из файла stdio.h, которые обычно реализованы как макросы, становятся соответствующими реентерабельными безопасными функциями.
Включение файла pthread.h предоставляет другие прототипы и определения, которые нужны в вашем программном коде, во многом так же, как делает stdio.h для подпрограмм стандартного ввода и вывода. В заключение следует убедиться в том, что вы включили в программу соответствующий заголовочный файл потоков и скомпоновали программу с подходящей библиотекой потоков, в которой реализованы функции семейства
int pthread_create(pthread_t * thread, pthread_attr_t *attr,
Прототип выглядит внушительно, но функцию очень легко применять. Первый аргумент — указатель на переменную типа
Предыдущая строка просто говорит о том, что вы должны передать адрес функции, принимающей бестиповой указатель
Возвращаемое значение равно 0 в случае успеха и номеру ошибки, если что-то пошло не так. В интерактивном справочном руководстве есть подробная информация об ошибочных ситуациях для этой и других функций, применяемых в данной главе.
mingw-w64 threads: posix vs win32
I’m installing mingw-w64 on Windows and there are two options: win32 threads and posix threads. I know what is the difference between win32 threads and pthreads but I don’t understand what is the difference between these two options. I doubt that if I will choose posix threads it will prevent me from calling WinAPI functions like CreateThread.
It seems that this option specify which threading API will be used by some program or library, but by what? By GCC, libstdc++ or by something else?
In short, for this version of mingw, the threads-posix release will use the posix API and allow the use of std::thread, and the threads-win32 will use the win32 API, and disable the std::thread part of the standard.
Ok, if I will select win32 threads then std::thread will be unavailable but win32 threads will still be used. But used by what?
4 Answers 4
GCC comes with a compiler runtime library (libgcc) which it uses for (among other things) providing a low-level OS abstraction for multithreading related functionality in the languages it supports. The most relevant example is libstdc++’s C++11 <thread> , <mutex> , and <future> , which do not have a complete implementation when GCC is built with its internal Win32 threading model. MinGW-w64 provides a winpthreads (a pthreads implementation on top of the Win32 multithreading API) which GCC can then link in to enable all the fancy features.
I must stress this option does not forbid you to write any code you want (it has absolutely NO influence on what API you can call in your code). It only reflects what GCC’s runtime libraries (libgcc/libstdc++/. ) use for their functionality. The caveat quoted by @James has nothing to do with GCC’s internal threading model, but rather with Microsoft’s CRT implementation.
- posix : enable C++11/C11 multithreading features. Makes libgcc depend on libwinpthreads, so that even if you don’t directly call pthreads API, you’ll be distributing the winpthreads DLL. There’s nothing wrong with distributing one more DLL with your application.
- win32 : No C++11 multithreading features.
Neither have influence on any user code calling Win32 APIs or pthreads APIs. You can always use both.
POSIX Threads for Windows
An implementation of the POSIX threads API for Windows
- Summary
- Files
- Reviews
- Support
- Wiki
- Code
Also known as "pthreads-win32", POSIX Threads for Windows implements a large subset of the threads related API from the Single Unix Specification Version 3.
Conformance and quality are high priorities of this mature library. Development began in 1998 and has continued with numerous significant professional contributions.
Please note:- whilst PThreads4W can be built and run by it, MinGW64 includes it's own default POSIX Threads library called "winpthreads". The two are not compatible and in order to build and run PThreads4W (formerly PThreads-WIn32) MinGW64 must be installed without win32pthreads. If you want or need to build and run with PThreads4W then you need to choose win32 threading instead of POSIX when you install MinGW64 to not install the conflicting winpthreads include and library files.
POSIX Threads for Windows
An implementation of the POSIX threads API for Windows
- Summary
- Files
- Reviews
- Support
- Wiki
- Code
Also known as "pthreads-win32", POSIX Threads for Windows implements a large subset of the threads related API from the Single Unix Specification Version 3.
Conformance and quality are high priorities of this mature library. Development began in 1998 and has continued with numerous significant professional contributions.
Please note:- whilst PThreads4W can be built and run by it, MinGW64 includes it's own default POSIX Threads library called "winpthreads". The two are not compatible and in order to build and run PThreads4W (formerly PThreads-WIn32) MinGW64 must be installed without win32pthreads. If you want or need to build and run with PThreads4W then you need to choose win32 threading instead of POSIX when you install MinGW64 to not install the conflicting winpthreads include and library files.
mingw-w64 threads: posix vs win32
GCC comes with a compiler runtime library (libgcc) which it uses for (among other things) providing a low-level OS abstraction for multithreading related functionality in the languages it supports. The most relevant example is libstdc++’s C++11 <thread> , <mutex> , and <future> , which do not have a complete implementation when GCC is built with its internal Win32 threading model. MinGW-w64 provides a winpthreads (a pthreads implementation on top of the Win32 multithreading API) which GCC can then link in to enable all the fancy features.
I must stress this option does not forbid you to write any code you want (it has absolutely NO influence on what API you can call in your code). It only reflects what GCC’s runtime libraries (libgcc/libstdc++/. ) use for their functionality. The caveat quoted by @James has nothing to do with GCC’s internal threading model, but rather with Microsoft’s CRT implementation.
- posix : enable C++11/C11 multithreading features. Makes libgcc depend on libwinpthreads, so that even if you don’t directly call pthreads API, you’ll be distributing the winpthreads DLL. There’s nothing wrong with distributing one more DLL with your application.
- win32 : No C++11 multithreading features.
Neither have influence on any user code calling Win32 APIs or pthreads APIs. You can always use both.
Solution 2
Parts of the GCC runtime (the exception handling, in particular) are dependent on the threading model being used. So, if you’re using the version of the runtime that was built with POSIX threads, but decide to create threads in your own code with the Win32 APIs, you’re likely to have problems at some point.
Even if you’re using the Win32 threading version of the runtime you probably shouldn’t be calling the Win32 APIs directly. Quoting from the MinGW FAQ:
As MinGW uses the standard Microsoft C runtime library which comes with Windows, you should be careful and use the correct function to generate a new thread. In particular, the CreateThread function will not setup the stack correctly for the C runtime library. You should use _beginthreadex instead, which is (almost) completely compatible with CreateThread .
Solution 3
Note that it is now possible to use some of C++11 std::thread in the win32 threading mode. These header-only adapters worked out of the box for me: https://github.com/meganz/mingw-std-threads
From the revision history it looks like there is some recent attempt to make this a part of the mingw64 runtime.
Solution 4
@rubenvb answer is fully correct, use the mingw posix compiler if you want to use std::thread , std::mutex , etc. For everybody who is using CMake, here is an example:
mingw-w64 threads: posix vs win32
I’m installing mingw-w64 on Windows and there are two options: win32 threads and posix threads. I know what is the difference between win32 threads and pthreads but I don’t understand what is the difference between these two options. I doubt that if I will choose posix threads it will prevent me from calling WinAPI functions like CreateThread.
It seems that this option specify which threading API will be used by some program or library, but by what? By GCC, libstdc++ or by something else?
In short, for this version of mingw, the threads-posix release will use the posix API and allow the use of std::thread, and the threads-win32 will use the win32 API, and disable the std::thread part of the standard.
Ok, if I will select win32 threads then std::thread will be unavailable but win32 threads will still be used. But used by what?
4 Answers 4
GCC comes with a compiler runtime library (libgcc) which it uses for (among other things) providing a low-level OS abstraction for multithreading related functionality in the languages it supports. The most relevant example is libstdc++’s C++11 <thread> , <mutex> , and <future> , which do not have a complete implementation when GCC is built with its internal Win32 threading model. MinGW-w64 provides a winpthreads (a pthreads implementation on top of the Win32 multithreading API) which GCC can then link in to enable all the fancy features.
I must stress this option does not forbid you to write any code you want (it has absolutely NO influence on what API you can call in your code). It only reflects what GCC’s runtime libraries (libgcc/libstdc++/. ) use for their functionality. The caveat quoted by @James has nothing to do with GCC’s internal threading model, but rather with Microsoft’s CRT implementation.
- posix : enable C++11/C11 multithreading features. Makes libgcc depend on libwinpthreads, so that even if you don’t directly call pthreads API, you’ll be distributing the winpthreads DLL. There’s nothing wrong with distributing one more DLL with your application.
- win32 : No C++11 multithreading features.
Neither have influence on any user code calling Win32 APIs or pthreads APIs. You can always use both.
Что такое posix win threads для Windows
Это приложение Windows под названием POSIX Threads for Windows, последнюю версию которого можно загрузить как pthreads4w-code-v3.0.0.zip. Его можно запустить онлайн на бесплатном хостинг-провайдере OnWorks для рабочих станций.
Загрузите и запустите онлайн это приложение под названием POSIX Threads для Windows с OnWorks бесплатно.
Следуйте этим инструкциям, чтобы запустить это приложение:
– 1. Загрузили это приложение на свой компьютер.
— 3. Загрузите это приложение в такой файловый менеджер.
— 4. Запустите любой онлайн-эмулятор OS OnWorks с этого веб-сайта, но лучше онлайн-эмулятор Windows.
– 6. Загрузите приложение и установите его.
— 7. Загрузите Wine из репозиториев программного обеспечения вашего дистрибутива Linux. После установки вы можете дважды щелкнуть приложение, чтобы запустить его с помощью Wine. Вы также можете попробовать PlayOnLinux, необычный интерфейс поверх Wine, который поможет вам установить популярные программы и игры для Windows.
Wine — это способ запуска программного обеспечения Windows в Linux, но без использования Windows. Wine — это слой совместимости с Windows с открытым исходным кодом, который может запускать программы Windows непосредственно на любом рабочем столе Linux. По сути, Wine пытается заново реализовать достаточно Windows с нуля, чтобы он мог запускать все эти приложения Windows, фактически не нуждаясь в Windows.
Потоки POSIX для Windows
ОПИСАНИЕ:
Потоки POSIX для Windows, также известные как «pthreads-win32», реализуют большое подмножество связанных с потоками API из Единой спецификации Unix версии 3.
Соответствие и качество являются главными приоритетами этой зрелой библиотеки. Разработка началась в 1998 году и продолжилась благодаря многочисленным значительным профессиональным вкладам.
Обратите внимание: хотя PThreads4W может быть собран и запущен с его помощью, MinGW64 включает собственную библиотеку потоков POSIX по умолчанию, которая называется «winpthreads». Они несовместимы, и для сборки и запуска PThreads4W (ранее PThreads-WIn32) необходимо установить MinGW64 без win32pthreads. Если вы хотите или должны собрать и запустить с PThreads4W, вам нужно выбрать потоки win32 вместо POSIX при установке MinGW64, чтобы не устанавливать конфликтующие файлы включения и библиотеки winpthreads.
Я устанавливаю mingw-w64 в Windows, и есть два варианта: потоки win32 и потоки posix. Я знаю, в чем разница между потоками win32 и pthreads, но я не понимаю, в чем разница между этими двумя вариантами. Я сомневаюсь, что если я выберу потоки posix, это не позволит мне вызывать функции WinAPI, такие как CreateThread.
Кажется, этот параметр указывает, какой API потоков будет использоваться какой-либо программой или библиотекой, но какой? С помощью GCC, libstdc++ или чего-то еще?
Короче говоря, для этой версии mingw релиз threads-posix будет использовать posix API и разрешать использование std::thread, а threads-win32 будет использовать API win32 и отключит std. ::thread часть стандарта.
Хорошо, если я выберу потоки win32, то std::thread будет недоступен, но потоки win32 все равно будут использоваться. Но для чего?
@devnull, разве это не определяется API, который я буду использовать? Если я выберу версию MinGW для pthreads, что помешает мне использовать WinAPI для потоков?
4 ответа 4
GCC поставляется с библиотекой времени выполнения компилятора (libgcc), которую он использует (среди прочего) для обеспечения низкоуровневой абстракции ОС для функций, связанных с многопоточностью, на поддерживаемых им языках. Наиболее подходящим примером являются C++11 , , и libstdc++, которые не имеют полной реализации, когда GCC построен с его внутренней моделью потоков Win32. MinGW-w64 предоставляет winpthreads (реализация pthreads поверх API многопоточности Win32), которую GCC затем может связать, чтобы включить все необычные функции.
Я должен подчеркнуть, что эта опция не запрещает вам писать любой код, который вы хотите (она абсолютно НИКАК не влияет на то, какой API вы можете вызывать в своем коде). Он отражает только то, что библиотеки времени выполнения GCC (libgcc/libstdc++/. ) используют для своей функциональности. Предостережение, процитированное @James, не имеет ничего общего с моделью внутренней многопоточности GCC, а скорее с реализацией Microsoft CRT.
- posix : включить функции многопоточности C++11/C11. Делает libgcc зависимым от libwinpthreads, поэтому, даже если вы не вызываете API pthreads напрямую, вы будете распространять DLL winpthreads. Нет ничего плохого в распространении еще одной DLL вместе с вашим приложением.
- win32: функции многопоточности C++11 отсутствуют.
Ни то, ни другое не влияет на пользовательский код, вызывающий API Win32 или API pthreads. Вы всегда можете использовать оба варианта.
Библиотеки потоков POSIX представляют собой основанный на стандартах API потоков для C/C++. Это позволяет создать новый поток параллельных процессов. Он наиболее эффективен в многопроцессорных или многоядерных системах, где поток процессов можно запланировать для запуска на другом процессоре, что позволяет увеличить скорость за счет параллельной или распределенной обработки.Потоки требуют меньше накладных расходов, чем «разветвление» или порождение нового процесса, потому что система не инициализирует новое системное пространство виртуальной памяти и среду для процесса. Хотя это наиболее эффективно в многопроцессорной системе, выигрыш также наблюдается в однопроцессорных системах, которые используют задержку ввода-вывода и других системных функций, которые могут остановить выполнение процесса. (Один поток может выполняться, в то время как другой ожидает ввода-вывода или некоторой другой системной задержки.) Технологии параллельного программирования, такие как MPI и PVM, используются в распределенной вычислительной среде, в то время как потоки ограничены одной компьютерной системой. Все потоки внутри процесса используют одно и то же адресное пространство. Поток создается путем определения функции и ее аргументов, которые будут обрабатываться в потоке. Целью использования библиотеки потоков POSIX в вашем программном обеспечении является ускорение выполнения программного обеспечения.
- Компилятор C: cc -lpthread pthread1.c
или - Компилятор C++: g++ -lpthread pthread1.c
-
В этом примере одна и та же функция используется в каждом потоке. Аргументы разные. Функции не обязательно должны быть одинаковыми.
- поток — возвращает идентификатор потока. (беззнаковое длинное целое, определенное в bits/pthreadtypes.h)
- attr — установите значение NULL, если используются атрибуты потока по умолчанию. (иначе определите члены структуры pthread_attr_t, определенной в bits/pthreadtypes.h) Атрибуты включают:
- отсоединенное состояние (можно присоединиться? По умолчанию: PTHREAD_CREATE_JOINABLE. Другой вариант: PTHREAD_CREATE_DETACHED)
- политика планирования (в режиме реального времени? PTHREAD_INHERIT_SCHED,PTHREAD_EXPLICIT_SCHED,SCHED_OTHER)
- параметр планирования
- унаследованный атрибут (по умолчанию: PTHREAD_EXPLICIT_SCHED Наследовать от родительского потока: PTHREAD_INHERIT_SCHED)
- scope (Потоки ядра: PTHREAD_SCOPE_SYSTEM Потоки пользователя: PTHREAD_SCOPE_PROCESS Выберите один или другой, а не оба.)
- размер защиты
- адрес стека (см. unistd.h и bits/posix_opt.h _POSIX_THREAD_ATTR_STACKADDR)
- размер стека (минимум PTHREAD_STACK_SIZE по умолчанию, установленный в pthread.h),
Эта процедура убивает поток. Функция pthread_exit никогда не возвращается. Если поток не отсоединен, идентификатор потока и возвращаемое значение могут быть проверены из другого потока с помощью pthread_join.
Примечание: указатель возврата *retval не должен иметь локальной области действия, иначе он перестанет существовать после завершения потока.Библиотека потоков предоставляет три механизма синхронизации:
- мьютексы — блокировка взаимного исключения: блокирует доступ к переменным для других потоков. Это обеспечивает монопольный доступ потока к переменной или набору переменных.
- joins — заставляет поток ждать, пока другие потоки не будут завершены (завершены).
- условные переменные — тип данных pthread_cond_t
Мьютексы:
Мьютексы используются для предотвращения несоответствия данных из-за условий гонки. Состояние гонки часто возникает, когда двум или более потокам необходимо выполнить операции над одной и той же областью памяти, но результаты вычислений зависят от порядка, в котором выполняются эти операции. Мьютексы используются для сериализации общих ресурсов. Каждый раз, когда доступ к глобальному ресурсу осуществляется более чем одним потоком, ресурс должен иметь связанный с ним мьютекс. Можно применить мьютекс для защиты сегмента памяти («критической области») от других потоков. Мьютексы могут применяться только к потокам в одном процессе и не работают между процессами, как семафоры.
Пример многопоточной функции:
Если операции загрузки и сохранения регистра для увеличения переменной counter происходят с неудачным временем, теоретически возможно, чтобы каждый поток увеличивал и перезаписывал одну и ту же переменную с одним и тем же значением. Другая возможность состоит в том, что второй поток сначала увеличит counter, блокируя первый поток до завершения, а затем первый поток увеличит его до 2.
Листинг кода: mutex1.c
Скомпилируйте: cc -lpthread mutex1.c
Выполните: ./a.out
Результаты:При попытке заблокировать мьютекс против мьютекса, удерживаемого другим потоком, этот поток блокируется до тех пор, пока мьютекс не будет разблокирован. Когда поток завершается, мьютекс не отключается, если он явно не разблокирован. По умолчанию ничего не происходит.
Присоединяется:
Соединение выполняется, когда нужно дождаться завершения потока. Процедура вызова потока может запустить несколько потоков, а затем дождаться их завершения, чтобы получить результаты. Один ждет завершения потоков с соединением.
Пример кода: join1.c
Скомпилировать: cc -lpthread join1.c
Выполнить: ./a.out
Результаты:Переменные условия:
Переменная условия – это переменная типа pthread_cond_t, которая используется с соответствующими функциями для ожидания и последующего продолжения процесса. Механизм условных переменных позволяет потокам приостанавливать выполнение и освобождать процессор до тех пор, пока некоторое условие не станет истинным. Переменная условия всегда должна быть связана с мьютексом, чтобы избежать состояния гонки, созданного одним потоком, готовящимся к ожиданию, и другим потоком, который может сигнализировать об условии до того, как первый поток фактически ожидает его, что приводит к взаимоблокировке. Поток будет постоянно ожидать сигнала, который никогда не будет отправлен. Можно использовать любой мьютекс, между мьютексом и условной переменной нет явной связи.
Функции, используемые в сочетании с условной переменной:
- Создание/удаление:
- pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
Пример кода: cond1.c
Скомпилировать: cc -lpthread cond1.c
Выполнить: ./a.out
Результаты:Обратите внимание, что функция functionCount1() была остановлена, пока значение count находилось между значениями COUNT_HALT1 и COUNT_HALT2. Единственное, что было гарантировано, это то, что functionCount2 будет увеличивать счетчик между значениями COUNT_HALT1 и COUNT_HALT2. Все остальное случайно.
Логические условия (операторы «if» и «while») должны быть выбраны, чтобы гарантировать, что «сигнал» будет выполнен, если «ожидание» когда-либо будет обработано. Плохая программная логика также может привести к взаимоблокировке.
Примечание. В этом примере изобилуют условия гонки, потому что в качестве условия используется count, и его нельзя заблокировать в инструкции while, не вызывая взаимной блокировки. Я буду работать над более чистым примером, но это пример условной переменной.
Если этот параметр включен, каждый поток может иметь свои собственные параметры планирования. Атрибуты расписания могут быть указаны:
- во время создания темы
- динамически изменяя атрибуты уже созданной цепочки
- путем определения влияния мьютекса на планирование потока при создании мьютекса
- путем динамического изменения расписания потока во время операций синхронизации.
-
Условия гонки: хотя код может отображаться на экране в том порядке, в котором вы хотите, чтобы код выполнялся, потоки планируются операционной системой и выполняются случайным образом. Нельзя предполагать, что потоки выполняются в порядке их создания. Они также могут выполняться с разной скоростью. Когда потоки выполняются (гонка за завершением), они могут давать неожиданные результаты (состояние гонки). Мьютексы и соединения должны использоваться для достижения предсказуемого порядка выполнения и результата.
Порядок применения мьютекса также важен. Следующий сегмент кода иллюстрирует потенциальную взаимоблокировку:
Если function1 получает первый мьютекс, а function2 — второй, все ресурсы связываются и блокируются.
-
— зарегистрировать обработчики, которые будут вызываться во время fork(2) [pthread_attr_init] — атрибуты создания потока [pthread_attr_init] — атрибуты создания потока [pthread_attr_init] — атрибуты создания потока [pthread_attr_init] — атрибуты создания потока [pthread_attr_init] — атрибуты создания потока [pthread_attr_init] — атрибуты создания потока — атрибуты создания потока [pthread_attr_init] — атрибуты создания потока [pthread_attr_init] — атрибуты создания потока [pthread_attr_init] — атрибуты создания потока [pthread_attr_init] — атрибуты создания потока [pthread_attr_init] — атрибуты создания потока — отмена потока [pthread_cleanup_push] — установить и удалить обработчики очистки [pthread_cleanup_push] — установить и удалить обработчики очистки — установить и удалить обработчики очистки [pthread_cleanup_push] — установить и удалить обработчики очистки [pthread_condattr_init] — атрибуты создания условия — атрибуты создания условия [pthread_cond_init] — операции над условиями [pthr ead_cond_init] — операции над условиями — операции над условиями [pthread_cond_init] — операции над условиями [pthread_cond_init] — операции над условиями [pthread_cond_init] — операции над условиями — создать новый поток — перевести работающий поток в состояние отсоединения — сравнить два идентификатора потока — завершить вызывающую нить [pthread_setschedparam] — управлять параметрами планирования нити [pthread_key_create] — управлять специфичными для нити данными — ждать завершения другой нити — управлять специфичными для нити данными [pthread_key_create] — управлять специфичными для нити данными — завершать все потоки в программе, кроме вызывающего потока [pthread_sigmask] — обработка сигналов в потоках [pthread_mutexattr_init] — атрибуты создания мьютекса [pthread_mutexattr_init] — атрибуты создания мьютекса — атрибуты создания мьютекса [pthread_mutexattr_init] — атрибуты создания мьютекса [pthread_mutex_init] — операции над мьютексами — операции над мьютексы [pthread_mutex_init] — операции над m utexes [pthread_mutex_init] — операции над мьютексами [pthread_mutex_init] — операции над мьютексами — однократная инициализация — возвращаемый идентификатор текущего потока [pthread_cancel] — отмена потока [pthread_cancel] — отмена потока — управление параметрами планирования потока [pthread_key_create] — управление потоком -специфические данные — обработка сигналов в потоках [pthread_cancel] — отмена потока
-
— Paul Mazzucco — Andrae Muys — Не Linux, но хороший справочник. — семафоры, условные переменные — Обсуждение того, как работают потоки Linux
- Независимые от платформы потоки:
-
— Абстракция потока; включая мьютексы, условия и частные данные потока. [пример]
-
— поддержка многопоточности, сокетов, доступа к файлам, демонов, постоянства, последовательного ввода-вывода, синтаксического анализа XML и системных служб — интерфейс C++
- Руководство для программистов ACE: [pdf] (темы см. на стр. 29)
- comp.programming.threads
- comp.unix.solaris
Охватывает представление ACE (ADAPTIVE Communication Environment) с открытым исходным кодом для потоков и других тем.
Бесплатная подписка на главный ресурс для профессиональных программистов и разработчиков программного обеспечения. Многоязычный и мультиплатформенный со списками программ, советами по программированию, обсуждением проблем дизайна и алгоритмами. Подпишитесь здесь!
Потоки можно использовать для реализации параллелизма. Для систем на базе UNIX стандартизированный интерфейс программирования потоков на языке C определен стандартом IEEE POSIX 1003.1c. Реализации, соответствующие этому стандарту, называются потоками POSIX или Pthreads.
Windows не поддерживает pthreads напрямую, вместо этого проект Pthreads-w32 стремится предоставить переносимую реализацию оболочки с открытым исходным кодом.
Первый шаг — получить проект pthread со следующего ftp-сайта:
И извлеките его в любое место по вашему выбору:
Это будет место, где будут расположены зависимости вашего проекта и дополнительные файлы библиотеки.
Чтобы продемонстрировать пример использования pthreads в среде Microsoft Visual Studio, сначала создайте новый пустой проект:
Добавьте исходный файл main.cpp в пустой проект и используйте следующий пример кода:
Теперь установите зависимости проекта, указав, где искать включаемые файлы.
Щелкните правой кнопкой мыши папку проекта и выберите «Свойства». В C/C++ > Общие > Дополнительные включаемые каталоги укажите расположение папки, в которой находятся включаемые потоки:
Затем нам нужно установить библиотеки. В свойствах проекта выберите Linker > General и задайте расположение файлов библиотеки pthread. Обязательно различайте 32- и 64-разрядные версии в зависимости от вашего ПК:
Наконец нам нужно установить дополнительные зависимости файла библиотеки. В свойствах проекта выберите Linker > Input > Additional Dependencies. Добавьте файлы pthreadVC2.lib и pthreadVCE2.lib:
Последний шаг — включение файла dll в проект. Узнайте, где находится pthreadVC2.dll:
Скопируйте этот файл отсюда и вставьте его туда, где находится ваш проект C++: