Threads windows что это

Windows Threads

Общая схема использования потоков Windows Threads

Для создания дополнительного потока необходимо вызывать функцию CreateThread , в качестве аргументов этой функции передаются приоритет запускаемого потока, функция, которую он будет исполнять, и ее аргументы. Ниже представлен пример создания дополнительного потока:

Для создания нескольких дочерних потоков необходимо несколько раз вызвать метод CreateThread

При разработке параллельных приложений на основе явного использования потоков операционной системы Windows важным аспектом является организация согласованного доступа к ресурсам, которые используются несколькими потоками, будь это общие переменные или участки памяти. Такие ресурсы называются общими или разделяемыми. При наличии нескольких потоков необходимо осуществлять синхронизацию их работы – это можно сделать с помощью таких механизмов операционной системы как критические секции, мьютексы, семафоры, события и другие.

Важные положительные моменты технологии Windows threads:

  • Разработка параллельных приложений на основе данной технологии позволяет явно управлять созданием и завершением потоков. Таким образом, обеспечивается контроль за началом и окончанием работы потоков.
  • Явное задание начальных данных и способов их обработки потоками.
  • Возможность достижения более высокого ускорения по сравнению с использованием технологии OpenMP.

Одним из недостатков данной технологии является необходимость реализации согласованного доступа к общим ресурсам и синхронизации выполнения потоков с помощью системных или пользовательских механизмов.

Потоки (threads) в WinAPI

Когда приложение начинает свою работу, для него создаётся процесс (process). Обычно, каждой программе соответствует один процесс.

При создании процесса для него выделяется память — виртуальное адресное пространство (virtual address space). Когда в отладчике мы смотрим на адреса переменных — мы видим адреса из этого пространства.

Потоки (Threads)

Каждый процесс имеет как минимум один поток (thread). До сих пор наши программы состояли из одного процесса и одного потока. В этом уроке мы научимся создавать дополнительные потоки в процессе.

Самый главный вопрос о потоках: для чего нужно несколько потоков? Разные потоки могут одновременно выполняться разными ядрами процессора. Например, в нашей однопоточной программе одна функция просчитывает искусственный интеллект, а другая — физику взаимодействия объектов. В этом случае сначала будет выполнена одна функция, а потом вторая. Если же мы разделим программу на два процесса и запустим программу на двухъядерном процессоре, то искусственный интеллект и физика будут просчитываться одновременно.

Все потоки имеют доступ к адресному пространству процесса. И это может стать серьёзной проблемой. Например, один поток обрабатывает данные и, одновременно, второй пытается вывести эти же данные на экран. Что произойдёт? Успеет ли первый поток обработать все данные, до того как до них доберётся второй поток? Или же второй поток обгонит первый, и часть данных пользователь увидит обработанными, а часть — нет? Эти вопросы мы обсудим в следующих уроках.

С точки зрения C++ поток — это обычная функция имеющая определённый прототип. Для создания потока используется функция CreateThread.

Создание потоков — CreateThread

Функция CreateThread возвращает описатель потока:

!1?HANDLE WINAPI CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId );?1!

1. lpThreadAttributes — данный аргумент определяет, может ли создаваемый поток быть унаследован дочерним процессом. Мы не будем создавать дочерние процессы, поэтому ставим NULL.

2. dwStackSize — размер стека в байтах. Если передать 0, то будет использоваться значение по-умолчанию (1 мегабайт).

3. lpStartAddress — адрес функции, которая будет выполняться потоком. Т.е. можно сказать, что функция, адрес которой передаётся в этот аргумент, является создаваемым потоком. Данная функция должна соответствовать определённому прототипу — рассмотрим ниже. Имя функции может быть любым — вы сами его выбираете.

4. lpParameter — указатель на переменную, которая будет передана в поток.

5. dwCreationFlags — флаги создания. Здесь можно отложить запуск выполнения потока. Мы будем запускать поток сразу же, передаём 0.

6. lpThreadId — указатель на переменную, куда будет сохранён идентификатор потока. Нам идентификатор не нужен, передаём NULL.

Давайте посмотрим на код вызова CreateThread:

!1?HANDLE thread = CreateThread(NULL,0,thread2,NULL, 0, NULL);?1!

Здесь мы сохраняем описатель потока в переменной thread. Обратите внимание на третий аргумент — адрес функции потока. thread2 — имя функции, которая и будет являться вторым потоком. Вот её код:

Функция потока должна соответствовать следующему прототипу:

!1?DWORD WINAPI ThreadProc(LPVOID lpParameter)?1!

Аргумент, который может принимать данная функция передаётся четвёртым параметров функции CreateThread. Если отбросить все переопределения типов, то данный прототип выглядит так:

!1?unsigned long __stdcall ThreadProc(void* lpParameter)?1!

Напоследок рассмотрим пример создания второго потока:

Создание программы с двумя потоками

Код программы можно скачать в начале урока. Это простая консольная программа. Для работы с потоками необходимо включить файл windows.h. Рассмотрим основной код:

!1?DWORD WINAPI thread2(LPVOID); int main() < cout

Потоки и работа с ними Threads and threading

Многопоточность позволяет увеличивать скорость реагирования приложения и, если приложение работает в многопроцессорной или многоядерной системе, его пропускную способность. Multithreading allows you to increase the responsiveness of your application and, if your application runs on a multiprocessor or multi-core system, increase its throughput.

Процессы и потоки Processes and threads

Процесс — это исполнение программы. A process is an executing program. Операционная система использует процессы для разделения исполняемых приложений. An operating system uses processes to separate the applications that are being executed. Поток — это основная единица, которой операционная система выделяет время процессора. A thread is the basic unit to which an operating system allocates processor time. Каждый поток имеет приоритет планирования и набор структур, в которых система сохраняет контекст потока, когда выполнение потока приостановлено. Each thread has a scheduling priority and maintains a set of structures the system uses to save the thread context when the thread’s execution is paused. Контекст потока содержит все сведения, позволяющие потоку безболезненно возобновить выполнение, в том числе набор регистров процессора и стек потока. The thread context includes all the information the thread needs to seamlessly resume execution, including the thread’s set of CPU registers and stack. Несколько потоков могут выполняться в контексте процесса. Multiple threads can run in the context of a process. Все потоки процесса используют общий диапазон виртуальных адресов. All threads of a process share its virtual address space. Поток может исполнять любую часть программного кода, включая части, выполняемые в данный момент другим потоком. A thread can execute any part of the program code, including parts currently being executed by another thread.

Платформа .NET Framework предоставляет способ изоляции приложений в процессе с помощью доменов приложений. .NET Framework provides a way to isolate applications within a process with the use of application domains. (Домены приложений недоступны в .NET Core.) Дополнительные сведения см. в разделе Домены приложений и потоки в статье Домены приложений. (Application domains are not available on .NET Core.) For more information, see the Application domains and threads section of the Application domains article.

По умолчанию программа .NET запускается с одним потоком, часто называемым основным потоком. By default, a .NET program is started with a single thread, often called the primary thread. Тем не менее она может создавать дополнительные потоки для выполнения кода параллельно или одновременно с основным потоком. However, it can create additional threads to execute code in parallel or concurrently with the primary thread. Эти потоки часто называются рабочими потоками. These threads are often called worker threads.

Цели применения нескольких потоков When to use multiple threads

Используйте несколько потоков, чтобы увеличить скорость реагирования приложения и воспользоваться преимуществами многопроцессорной или многоядерной системы, чтобы увеличить пропускную способность приложения. You use multiple threads to increase the responsiveness of your application and to take advantage of a multiprocessor or multi-core system to increase the application’s throughput.

Представьте себе классическое приложение, в котором основной поток отвечает за элементы пользовательского интерфейса и реагирует на действия пользователя. Consider a desktop application, in which the primary thread is responsible for user interface elements and responds to user actions. Используйте рабочие потоки для выполнения длительных операций, которые, в противном случае будут занимать основной поток, в результате чего пользовательский интерфейс будет недоступен. Use worker threads to perform time-consuming operations that, otherwise, would occupy the primary thread and make the user interface non-responsive. Для более оперативной реакции на входящие сообщения или события также можно использовать выделенный поток связи с сетью или устройством. You can also use a dedicated thread for network or device communication to be more responsive to incoming messages or events.

Если программа выполняет операции, которые могут выполняться параллельно, можно уменьшить общее время выполнения путем выполнения этих операций в отдельных потоках и запуска программы в многопроцессорной или многоядерной системе. If your program performs operations that can be done in parallel, the total execution time can be decreased by performing those operations in separate threads and running the program on a multiprocessor or multi-core system. В такой системе использование многопоточности может увеличить пропускную способность, а также повысить скорость реагирования. On such a system, use of multithreading might increase throughput along with the increased responsiveness.

Как использовать многопоточность в .NET How to use multithreading in .NET

Начиная с .NET Framework 4, для многопоточности рекомендуется использовать библиотеку параллельных задач (TPL) и Parallel LINQ (PLINQ). Starting with .NET Framework 4, the recommended way to utilize multithreading is to use Task Parallel Library (TPL) and Parallel LINQ (PLINQ). Дополнительные сведения см. в разделе Параллельное программирование. For more information, see Parallel programming.

Библиотека параллельных задач и PLINQ полагаются на потоки ThreadPool. Both TPL and PLINQ rely on the ThreadPool threads. Класс System.Threading.ThreadPool предоставляет приложения .NET с пулом рабочих потоков. The System.Threading.ThreadPool class provides a .NET application with a pool of worker threads. Также можно использовать потоки из пула потоков. You can also use thread pool threads. Дополнительные сведения см. в разделе Управляемый пул потоков. For more information, see The managed thread pool.

Наконец, можно использовать класс System.Threading.Thread, который представляет управляемый поток. At last, you can use the System.Threading.Thread class that represents a managed thread. Дополнительные сведения см. в разделе Использование потоков и работа с потоками. For more information, see Using threads and threading.

Несколько потоков могут требовать доступ к общему ресурсу. Multiple threads might need to access a shared resource. Чтобы сохранить ресурс в неповрежденном состоянии и избежать состояния гонки, необходимо синхронизировать доступ к нему потоков. To keep the resource in a uncorrupted state and avoid race conditions, you must synchronize the thread access to it. Вы также можете координировать взаимодействие нескольких потоков. You also might want to coordinate the interaction of multiple threads. Платформа .NET предоставляет ряд типов для синхронизации доступа к общему ресурсу или координации взаимодействия потоков. .NET provides a range of types that you can use to synchronize access to a shared resource or coordinate thread interaction. Дополнительные сведения см. в разделе Обзор примитивов синхронизации. For more information, see Overview of synchronization primitives.

Исключения следует обрабатывать в потоках. Do handle exceptions in threads. Необработанные исключения в потоках, как правило, приводят к завершению процесса. Unhandled exceptions in threads generally terminate the process. Дополнительные сведения см. в статье Исключения в управляемых потоках. For more information, see Exceptions in managed threads.

Управляемые и неуправляемые потоки в Windows Managed and unmanaged threading in Windows

Управление всеми потоками осуществляется посредством класса Thread , включая потоки, созданные средой CLR или созданные за пределами среды выполнения и входящие в управляемую среду для выполнения кода. Management of all threads is done through the Thread class, including threads created by the common language runtime and those created outside the runtime that enter the managed environment to execute code. Среда выполнения отслеживает в своем процессе все потоки, которые когда-либо выполняли код в управляемой среде. The runtime monitors all the threads in its process that have ever executed code within the managed execution environment. Другие потоки она не отслеживает. It does not track any other threads. Потоки могут входить в управляемую среду выполнения посредством COM-взаимодействия (так как среда выполнения предоставляет управляемые объекты неуправляемой среде в качестве COM-объектов), функции COM DllGetClassObject и вызова неуправляемого кода. Threads can enter the managed execution environment through COM interop (because the runtime exposes managed objects as COM objects to the unmanaged world), the COM DllGetClassObject function, and platform invoke.

Когда неуправляемый поток входит в среду выполнения, например, посредством вызываемой оболочки COM, система проверяет локальное хранилище потока данного потока для поиска внутреннего управляемого объекта Thread . When an unmanaged thread enters the runtime through, for example, a COM callable wrapper, the system checks the thread-local store of that thread to look for an internal managed Thread object. Если он найден, среда выполнения уже оповещена об этом потоке. If one is found, the runtime is already aware of this thread. Если найти объект не удается, среда выполнения создает новый объект Thread и устанавливает его в локальном хранилище потока данного потока. If it cannot find one, however, the runtime builds a new Thread object and installs it in the thread-local store of that thread.

При использовании управляемых потоков Thread.GetHashCode является стабильным средством идентификации управляемого потока. In managed threading, Thread.GetHashCode is the stable managed thread identification. В течение времени существования вашего потока он не будет конфликтовать со значением из любого другого потока независимо от того, из какого домена приложения вы получили это значение. For the lifetime of your thread, it will not collide with the value from any other thread, regardless of the application domain from which you obtain this value.

Сопоставление потоков Win32 с управляемыми потоками Mapping from Win32 threading to managed threading

В следующей таблице элементы потоков Win32 сопоставляются со своими ближайшими аналогами из среды выполнения. The following table maps Win32 threading elements to their approximate runtime equivalent. Обратите внимание, что такое сопоставление не означает идентичную функциональность. Note that this mapping does not represent identical functionality. Например, TerminateThread не выполняет предложения finally , не освобождает ресурсы и не может быть запрещен. For example, TerminateThread does not execute finally clauses or free up resources, and cannot be prevented. Однако Thread.Abort выполняет весь ваш код отката, освобождает все ресурсы и может быть отменен с помощью ResetAbort. However, Thread.Abort executes all your rollback code, reclaims all the resources, and can be denied using ResetAbort. Прежде чем делать предположения о функциональности, тщательно изучите документацию. Be sure to read the documentation closely before making assumptions about functionality.

В Win32 In Win32 В среде CLR In the common language runtime
CreateThread CreateThread Сочетание Thread и ThreadStart Combination of Thread and ThreadStart
TerminateThread TerminateThread Thread.Abort
SuspendThread SuspendThread Thread.Suspend
ResumeThread ResumeThread Thread.Resume
Sleep Sleep Thread.Sleep
WaitForSingleObject в дескрипторе потока WaitForSingleObject on the thread handle Thread.Join
ExitThread ExitThread Эквивалент отсутствует No equivalent
GetCurrentThread GetCurrentThread Thread.CurrentThread
SetThreadPriority SetThreadPriority Thread.Priority
Эквивалент отсутствует No equivalent Thread.Name
Эквивалент отсутствует No equivalent Thread.IsBackground
Близко к CoInitializeEx (OLE32.DLL) Close to CoInitializeEx (OLE32.DLL) Thread.ApartmentState

Управляемые потоки и подразделения COM Managed threads and COM apartments

Управляемый поток может быть отмечен для указания того, что в нем будет размещаться однопотоковое или многопотоковое подразделение. A managed thread can be marked to indicate that it will host a single-threaded or multithreaded apartment. (Дополнительные сведения об архитектуре потоков COM см. в статье Processes, Threads, and Apartments (Процессы, потоки и подразделения)). Методы GetApartmentState, SetApartmentState и TrySetApartmentState класса Thread возвращают и назначают состояние подразделения потока. (For more information on the COM threading architecture, see Processes, Threads, and Apartments.) The GetApartmentState, SetApartmentState, and TrySetApartmentState methods of the Thread class return and assign the apartment state of a thread. Если состояние не задано, GetApartmentState возвращает ApartmentState.Unknown. If the state has not been set, GetApartmentState returns ApartmentState.Unknown.

Свойство можно задать, только когда поток находится в состоянии ThreadState.Unstarted ; его можно задать только один раз для потока. The property can be set only when the thread is in the ThreadState.Unstarted state; it can be set only once for a thread.

Если состояние подразделения не задано до запуска потока, этот поток инициализируется в качестве многопотокового подразделения (MTA). If the apartment state is not set before the thread is started, the thread is initialized as a multithreaded apartment (MTA). Поток метода завершения и все потоки, управляемые ThreadPool , являются многопотоковыми подразделениями. The finalizer thread and all threads controlled by ThreadPool are MTA.

Для кода запуска приложения единственный способ управления состоянием подразделения заключается в применении MTAThreadAttribute или STAThreadAttribute к процедуре точки входа. For application startup code, the only way to control apartment state is to apply the MTAThreadAttribute or the STAThreadAttribute to the entry point procedure.

Управляемые объекты, предоставляемые интерфейсу COM, работают так, как если бы они агрегировали упаковщик в режиме свободного потока. Managed objects that are exposed to COM behave as if they had aggregated the free-threaded marshaler. Другими словами, их можно вызвать из любого подразделения COM в режиме свободного потока. In other words, they can be called from any COM apartment in a free-threaded manner. В таком режиме не работают только управляемые объекты, производные от ServicedComponent или StandardOleMarshalObject. The only managed objects that do not exhibit this free-threaded behavior are those objects that derive from ServicedComponent or StandardOleMarshalObject.

В управляемом коде отсутствует поддержка SynchronizationAttribute , если только вы не используете контексты и контекстно-привязанные управляемые экземпляры. In the managed world, there is no support for the SynchronizationAttribute unless you use contexts and context-bound managed instances. Если вы используете корпоративные службы, ваш объект должен быть производным от ServicedComponent (который сам является производным от ContextBoundObject). If you are using Enterprise Services, then your object must derive from ServicedComponent (which is itself derived from ContextBoundObject).

Когда управляемый код вызывает COM-объекты, он всегда следует правилам COM. When managed code calls out to COM objects, it always follows COM rules. Другими словами, он выполняет вызов через прокси-серверы подразделения COM и оболочки контекста COM+ 1.0, как того требует OLE32. In other words, it calls through COM apartment proxies and COM+ 1.0 context wrappers as dictated by OLE32.

Блокирующие проблемы Blocking issues

Если поток выполняет неуправляемый вызов для операционной системы, которая заблокировала этот поток в неуправляемом коде, среда выполнения не берет на себя управление им для Thread.Interrupt или Thread.Abort. If a thread makes an unmanaged call into the operating system that has blocked the thread in unmanaged code, the runtime will not take control of it for Thread.Interrupt or Thread.Abort. В случае с Thread.Abortсреда выполнения помечает поток как Abort и берет управление, когда он повторно входит в управляемый код. In the case of Thread.Abort, the runtime marks the thread for Abort and takes control of it when it re-enters managed code. Вместо неуправляемой блокировки рекомендуется использовать управляемую блокировку. It is preferable for you to use managed blocking rather than unmanaged blocking. WaitHandle.WaitOne,WaitHandle.WaitAny, WaitHandle.WaitAll, Monitor.Enter, Monitor.TryEnter, Thread.Join, GC.WaitForPendingFinalizers и др. реагируют на Thread.Interrupt и Thread.Abort. WaitHandle.WaitOne,WaitHandle.WaitAny, WaitHandle.WaitAll, Monitor.Enter, Monitor.TryEnter, Thread.Join, GC.WaitForPendingFinalizers, and so on are all responsive to Thread.Interrupt and to Thread.Abort. Кроме того, если ваш поток находится в однопотоковом подразделении, все эти операции управляемой блокировки будут корректно выдавать сообщения в ваше подразделение, пока поток находится в заблокированном состоянии. Also, if your thread is in a single-threaded apartment, all these managed blocking operations will correctly pump messages in your apartment while your thread is blocked.

Потоки и волокна Threads and fibers

Потоковая модель .NET не поддерживает волокна. The .NET threading model does not support fibers. Не следует вызывать неуправляемые функции, которые реализуется с использованием волокон. You should not call into any unmanaged function that is implemented by using fibers. Такие вызовы могут привести к сбою среды выполнения .NET. Such calls may result in a crash of the .NET runtime.

Читайте также:  Киоск режим для windows
Оцените статью