- Windows Threads
- Общая схема использования потоков Windows Threads
- Processes, Threads, and Apartments
- The Apartment and the COM Threading Architecture
- Windows thread API in the C program
- Windows Kernel-Mode Process and Thread Manager
- Subsystem Processes
- Controlling Processes and Threads in WinDbg
- Opening the Processes and Threads Window
- Using the Processes and Threads Window
- Additional Information
Windows Threads
Общая схема использования потоков Windows Threads
Для создания дополнительного потока необходимо вызывать функцию CreateThread , в качестве аргументов этой функции передаются приоритет запускаемого потока, функция, которую он будет исполнять, и ее аргументы. Ниже представлен пример создания дополнительного потока:
Для создания нескольких дочерних потоков необходимо несколько раз вызвать метод CreateThread
При разработке параллельных приложений на основе явного использования потоков операционной системы Windows важным аспектом является организация согласованного доступа к ресурсам, которые используются несколькими потоками, будь это общие переменные или участки памяти. Такие ресурсы называются общими или разделяемыми. При наличии нескольких потоков необходимо осуществлять синхронизацию их работы – это можно сделать с помощью таких механизмов операционной системы как критические секции, мьютексы, семафоры, события и другие.
Важные положительные моменты технологии Windows threads:
- Разработка параллельных приложений на основе данной технологии позволяет явно управлять созданием и завершением потоков. Таким образом, обеспечивается контроль за началом и окончанием работы потоков.
- Явное задание начальных данных и способов их обработки потоками.
- Возможность достижения более высокого ускорения по сравнению с использованием технологии OpenMP.
Одним из недостатков данной технологии является необходимость реализации согласованного доступа к общим ресурсам и синхронизации выполнения потоков с помощью системных или пользовательских механизмов.
Processes, Threads, and Apartments
A process is a collection of virtual memory space, code, data, and system resources. A thread is code that is to be serially executed within a process. A processor executes threads, not processes, so each application has at least one process, and a process always has at least one thread of execution, known as the primary thread. A process can have multiple threads in addition to the primary thread.
Processes communicate with one another through messages, using Microsoft’s Remote Procedure Call (RPC) technology to pass information to one another. There is no difference to the caller between a call coming from a process on a remote machine and a call coming from another process on the same machine.
When a thread begins to execute, it continues until it is killed or until it is interrupted by a thread with higher priority (by a user action or the kernel’s thread scheduler). Each thread can run separate sections of code, or multiple threads can execute the same section of code. Threads executing the same block of code maintain separate stacks. Each thread in a process shares that process’s global variables and resources.
The thread scheduler determines when and how often to execute a thread, according to a combination of the process’s priority class attribute and the thread’s base priority. You set a process’s priority class attribute by calling the SetPriorityClass function , and you set a thread’s base priority with a call to SetThreadPriority.
Multithreaded applications must avoid two threading problems: deadlocks and races. A deadlock occurs when each thread is waiting for the other to do something. The COM call control helps prevent deadlocks in calls between objects. A race condition occurs when one thread finishes before another on which it depends, causing the former to use an uninitialized value because the latter has not yet supplied a valid one. COM supplies some functions specifically designed to help avoid race conditions in out-of-process servers. (See Out-of-Process Server Implementation Helpers.)
The Apartment and the COM Threading Architecture
While COM supports the single-thread-per-process model prevalent before the introduction of multiple threads of execution, you can write code to take advantage of multiple threads, resulting in more efficient applications, by allowing one thread to be executed while another thread waits for some time-consuming operation to complete.
Using multiple threads is not a guarantee of better performance. In fact, because thread factoring is a difficult problem, using multiple threads often causes performance problems. The key is to use multiple threads only if you are very sure of what you are doing.
In general, the simplest way to view the COM threading architecture is to think of all the COM objects in the process as divided into groups called apartments. A COM object lives in exactly one apartment, in the sense that its methods can legally be directly called only by a thread that belongs to that apartment. Any other thread that wants to call the object must go through a proxy.
- Single-threaded apartments consist of exactly one thread, so all COM objects that live in a single-threaded apartment can receive method calls only from the one thread that belongs to that apartment. All method calls to a COM object in a single-threaded apartment are synchronized with the windows message queue for the single-threaded apartment’s thread. A process with a single thread of execution is simply a special case of this model.
- Multithreaded apartments consist of one or more threads, so all COM objects that live in an multithreaded apartment can receive method calls directly from any of the threads that belong to the multithreaded apartment. Threads in a multithreaded apartment use a model called free-threading. Calls to COM objects in a multithreaded apartment are synchronized by the objects themselves.
For a description of communication between single-threaded apartments and multithreaded apartments within the same process, see Single-Threaded and Multithreaded Communication.
A process can have zero or more single-threaded apartments and zero or one multithreaded apartment.
In a process, the main apartment is the first to be initialized. In a single-threaded process, this is the only apartment. Call parameters are marshaled between apartments, and COM handles the synchronization through messaging. If you designate multiple threads in a process to be free-threaded, all free threads reside in a single apartment, parameters are passed directly to any thread in the apartment, and you must handle all synchronization. In a process with both free-threading and apartment threading, all free threads reside in a single apartment and all other apartments are single-threaded apartments. A process that does COM work is a collection of apartments with, at most, one multithreaded apartment but any number of single-threaded apartments.
The threading models in COM provide the mechanism for clients and servers that use different threading architectures to work together. Calls among objects with different threading models in different processes are naturally supported. From the perspective of the calling object, all calls to objects outside a process behave identically, no matter how the object being called is threaded. Likewise, from the perspective of the object being called, arriving calls behave identically, regardless of the threading model of the caller.
Interaction between a client and an out-of-process object is straightforward, even when they use different threading models because the client and object are in different processes. COM, interposed between the client and the server, can provide the code for the threading models to interoperate, using standard marshaling and RPC. For example, if a single-threaded object is called simultaneously by multiple free-threaded clients, the calls will be synchronized by COM by placing corresponding window messages in the server’s message queue. The object’s apartment will receive one call each time it retrieves and dispatches messages. However, some care must be taken to ensure that in-process servers interact properly with their clients. (See In-Process Server Threading Issues.)
The most important issue in programming with a multithreaded model is to make your code thread-safe so that messages intended for a particular thread go only to that thread and access to threads is protected.
For more information, see the following topics:
Windows thread API in the C program
Threads are created in the Windows API using the CreateThread() function, and—just as in Pthreads—a set of attributes like security information, the size of the stack, and a flag for the thread is passed to this function. In the below program, we use the default values for these attributes. (The default values do not initially set the thread to a suspended state and instead make it eligible to be run by the CPU scheduler.) Once the summation thread is created, the parent must wait for it to complete before outputting the value of Sum, as the value is set by the summation thread. In Pthread program, we had the parent thread wait for the summation thread using the pthread join() statement Here, using the WaitForSingleObject() function, we perform the equivalent of this in the Windows API , which causes the creating thread to block until the summation thread has exited. In situations that require waiting for multiple threads to complete, the WaitForMultipleObjects() function is used. This function is passed four parameters −
- The number of objects to wait for
- A pointer to the array of objects
- A flag indicating whether all objects have been signaled.
- A timeout duration (or INFINITE)
For example, if THandles is an array of thread HANDLE objects of size N, the parent thread can wait for all its child threads to complete with this statement −
WaitForMultipleObjects(N, THandles, TRUE, INFINITE);
Windows Kernel-Mode Process and Thread Manager
A process is a software program that is currently running in Windows. Every process has an ID, a number that identifies it. A thread is an object that identifies which part of the program is running. Each thread has an ID, a number that identifies it.
A process may have more than one thread. The purpose of a thread is to allocate processor time. On a machine with one processor, more than one thread can be allocated, but only one thread can run at a time. Each thread only runs a short time and then the execution is passed on to the next thread, giving the user the illusion that more than one thing is happening at once. On a machine with more than one processor, true multi-threading can take place. If an application has multiple threads, the threads can run simultaneously on different processors.
The Windows kernel-mode process and thread manager handles the execution of all threads in a process. Whether you have one processor or more, great care must be taken in driver programming to make sure that all threads of your process are designed so that no matter what order the threads are handled, your driver will operate properly.
If threads from different processes attempt to use the same resource at the same time, problems can occur. Windows provides several techniques to avoid this problem. The technique of making sure that threads from different processes do not touch the same resource is called synchronization. For more information about synchronization, see Synchronization Techniques.
Routines that provide a direct interface to the process and thread manager are usually prefixed with the letters «Ps«; for example, PsCreateSystemThread. For a list of kernel DDIs, see Windows kernel.
This set of guidelines applies to these callback routines:
- Keep routines short and simple.
- Do not make calls into a user mode service to validate the process, thread, or image.
- Do not make registry calls.
- Do not make blocking and/or Interprocess Communication (IPC) function calls.
- Do not synchronize with other threads because it can lead to reentrancy deadlocks.
- Use System Worker Threads to queue work especially work involving:
- Slow API’s or API’s that call into other process.
- Any blocking behavior which could interrupt threads in core services.
- If you use System Worker Threads do not wait on the work to complete. Doing so defeats the purpose of queuing the work to be completed asynchronously.
- Be considerate of best practices for kernel mode stack usage. For examples, see How do I keep my driver from running out of kernel-mode stack? and Key Driver Concepts and Tips.
Subsystem Processes
Starting in WindowsВ 10, the Windows Subsystem for Linux (WSL) enables a user to run native Linux ELF64 binaries on Windows, alongside other Windows applications. For information about WSL architecture and the user-mode and kernel-mode components that are required to run the binaries, see the posts on the Windows Subsystem for Linux blog.
One of the components is a subsystem process that hosts the unmodified user-mode Linux binary, such as /bin/bash. Subsystem processes do not contain data structures associated with Win32 processes, such as Process Environment Block (PEB) and Thread Environment Block (TEB). For a subsystem process, system calls and user mode exceptions are dispatched to a paired driver.
Here are the changes to the Process and Thread Manager Routines in order to support subsystem processes:
- The WSL type is indicated by the SubsystemInformationTypeWSL value in the SUBSYSTEM_INFORMATION_TYPE enumeration. Drivers can call NtQueryInformationProcess and NtQueryInformationThread to determine the underlying subsystem. Those calls return SubsystemInformationTypeWSL for WSL.
- Other kernel mode drivers can get notified about subsystem process creation/deletion by registering their callback routine through the PsSetCreateProcessNotifyRoutineEx2 call. To get notifications about thread creation/deletion, drivers can call PsSetCreateThreadNotifyRoutineEx, and specify PsCreateThreadNotifySubsystems as the type of notification.
- The PS_CREATE_NOTIFY_INFO structure has been extended to include a IsSubsystemProcess member that indicates a subsystem other than Win32. Other members such as FileObject, ImageFileName, CommandLine indicate additional information about the subsystem process. For information about the behavior of those members, see SUBSYSTEM_INFORMATION_TYPE.
—>
Controlling Processes and Threads in WinDbg
In WinDbg, the Processes and Threads window displays information about the systems, processes, and threads that are being debugged. This window also enables you to select a new system, process, and thread to be active.
Opening the Processes and Threads Window
To open the Processes and Threads window, choose Processes and Threads from the View menu. (You can also press ALT+9 or select the Processes and Threads button () on the toolbar. ALT+SHIFT+9 closes the Processes and Threads window.)
The following screen shot shows an example of a Processes and Threads window.
The Processes and Threads window displays a list of all processes that are currently being debugged. The threads in the process appear under each process. If the debugger is attached to multiple systems, the systems are shown at the top level of the tree, with the processes subordinate to them, and the threads subordinate to the processes.
Each system listing includes the server name and the protocol details. The system that the debugger is running on is identified as .
Each process listing includes the internal decimal process index that the debugger uses, the hexadecimal process ID, and the name of the application that is associated with the process.
Each thread listing includes the internal decimal thread index that the debugger uses and the hexadecimal thread ID.
Using the Processes and Threads Window
In the Processes and Threads window, the current or active system, process, and thread appear in bold type. To make a new system, process, or thread active, select its line in the window.
The Processes and Threads window has a shortcut menu with additional commands. To access the menu, select and hold (or right-click) the title bar or select the icon near the upper-right corner of the window (). The following list describes some of the menu commands:
Move to new dock closes the Processes and Threads window and opens it in a new dock.
Always floating causes the window to remain undocked even if it is dragged to a docking location.
Move with frame causes the window to move when the WinDbg frame is moved, even if the window is undocked.
Additional Information
For other methods of displaying or controlling systems, see Debugging Multiple Targets. For other methods of displaying or controlling processes and threads, see Controlling Processes and Threads.