Threads within Windows NT

Windows NT (also called Win32) uses threads for a variety of uses. Like many other time-sharing operating systems, Windows NT utilzies threads to help with Interprocess communications, Overlapped I/O, Security, and Synchronization. But, if you missed it, go here for a brief indroduction to Threads and Processes.

Windows NT is a multi-threaded environment in which each process contains at least one thread of execution. Most lengthy operations will take place in a separate background thread with low priority while the user continues to work in a separate, normal priority thread. Windows NT also will take advantage of a machine using multiple processors by running each thread on a separate CPU. For more information on how NT creates and maintains threads in general, please refer to the Windows NT code page.

IPC
Interprocess communications (IPC's) are exactly what they sound like: communication between two or more processes, often for the use of sharing data. Windows NT is designed and implemented in two ways and one must understand both in order to properly implement threading. Windows NT can be used on a stand-alone machine or a networked machine and each requires different IPC methods. On a stand-alone machine, the IPC methods that work are:

  • File Mapping

  • Shared Memory

  • Anonymous Pipes

  • The IPC methods that work on networked machines are:

  • NetBIOS

  • Mailslots

  • Named Pipes

  • OLE

  • TCP/IP and Windows Socket (winsock)

  • Threads are often a good way to manage IPC. Another example of IPC methods would be maintenance of the Dynamic-Link Libraries (DLL). One could use threads to keep memory allocation separated on a thread-by-thread basis with local thread storage within the computer's DLLs.

    Back to Top

    Overlapped I/O
    Overlapped I/O is a form of asynchronous file access. Threads are very useful when implementing overlapped I/O within NT as overlapping input/output allows the application to start an i/o operation and then continue while the i/o completes in the background. This does not necesarily require a separate thread but it does give the impression that multi-threading is performed. Overlapped I/O also allows two or more threads to access the same file at the same time. Within overlapped I/O, each thread maintains position information in the overlapped structure, which must be maintained manually after each I/O operation.

    For a more detailed examination, refer to the Windows NT code page.

    Back to Top

    Security
    Win32 Security API allows developers to grant or deny access to specific system objects and resources. If Windows NT is implemented on a server which has multiple users accessing its files, the developer can deny normal users access to sensitive system information. Now, recall that NT utilizes threads for file access and mapping (see IPC). When WinNT is implemented on a general server, such file access is controled through multi-threading. Each user has a thread with their unique security access key on it. If the user requests a resource that their thread does not have the key for, access will be denied by the Security API.

    Back to Top

    Synchronization
    If you viewed the simple java program on the main page, you would know that threads are often an integral part of synchronization. Such is true within Windows NT. Windows NT implements two typs of syncrhonization: mutual exclusion and signaling.

    Mutual exclusion involves denying other threads of execution access to critical sections of code. A critical section is a block of code that should be single-threaded, meaning only one thread should be executing that section of the code at any given time. Only when that thread has finished with that section of code should another thread enter it. The following portions of code are used to control it (for a more detailed look, go to the Windows NT Code page):

  • InitializeCriticalSection() - initializes a critical section object.

  • EnterCriticalSection() - marks the beginning of a critical section block of code. NT checks the critical section object and, if it is available, NT flags it as being used (increments the count) and returns. If it is already in use, NT suspends the thread that made the call. Once the section is available, NT resumes the thread. Multiple threads calling the same critical section are resumed in a first come, first served basis.

  • LeaveCriticalSection() - marks the end of the critical section block of code and decrements the "in use" count.

  • DeleteCriticalSection() - frees all resources used by an unused critical section object.

  • EnterCriticalSectoin() and LeaveCriticalSection() should, obviously, always be in pairs. Critical sections are only valid within the same process. The critical section object should only be initialized once. It should be used only for the purposes of a critical section and should not be modified except through the use of the above critical section calls.

    Along with threads, Windows NT also implements Mutexes and Semaphores. They often work together to keep the operating system running smoothly. For example, the WaitForSingleObject() call can be passed a mutex object:

    WaitForSingleObject( hdlMutex, INFINITE );

    Again, see the Windows NT Code page for a more complete example.

    If a thread already has ownership of a mutex, it can make additional wait function calls on the same mutex without blocking. This prevents the thread from deadlocking itself.

    That brings us to the greatest challenge of synchronization: prevention of deadlock. To facilitate the explanation, I've created a diagram.


    Thread A owns Mutex 1 and Thread B owns mutex 2. Thread A needs mutex 2 to complete its operation. However, Thread B needs mutex 1 to finish its processing. Thread A the blocks waiting for Mutex 2 while Thread B blocks waiting for Mutex 1. As more mutexes are added, the potential for deadlock increases.

    Another threat to synchronization is starvation.


    Thread A owns Mutex X and Mutex Y. Thread B owns Mutex Z and waits on Mutex Y. Thread A and B share Mutex Y. Thread C, however, needs Mutex Z and Mutex Z and shares them between Thread A and Thread B. Thread C is essentially blocked until both of the resources associated with Mutex X and Mutex Z are available. Threads A and B, given this setup, can essentially starve Thread C by never allowing it to run.

    One last thread to synchronization is race conditions.


    Three threads reading a shared resource simultaneously, this is allowed. However, if you want to protect against more than one thread modifying a resource concurrently, or against one thread modifying a resource while another thread is reading from it, Windows NT uses a single mutex, seriealizing the read request and reducing throughput. One example of how to achieve this is given in the Windows NT Code page. Windows NT denies write access to the reader while another person is writing to the shared resource.

    Back to Top

    Home