The behavior of the events API is different according to the type of events. When you use SetEvent on a manual event object, the event will stay signaled until ResetEvent is explicitly called. Auto-reset events only stay signaled until a single waiting thread is released. At most, one waiting thread is released when using the PulseEvent function on auto-reset events before it immediately transitions back to the non-signaled state. In the case of manual threads, all waiting threads are released and immediately transition back to a non-signaled state.
Interlocked Functions
In multithread environments, threads can be interrupted at any time and resumed later by the scheduler. Portions of code or applications resources can be protected using semaphores, events, or critical sections. In some applications, it could be too time consuming to use those kinds of system objects to protect only one line of code like this:
// Increment variable
dwMyVariable = dwMyVariable + 1;
The sample source code above in C is one single instruction, but in assembly it could be more than that. In this particular example, the thread can be suspended in the middle of the operation and resumed later, but errors can potentially be encountered in the case of another thread using the same variable. The operation is not atomic. Fortunately, it is possible in Windows Embedded CE 6.0 R2 to increment, decrement, and add values in multithreading-safe, atomic operations without using synchronization objects. This is done by using interlocked functions.
Table 3-15 lists the most important interlocked functions that are used to atomically manipulate variables.
Table 3-15 Interlock API
Function | Description |
---|---|
InterlockedIncrement | Increment the value of a 32 bit variable. |
InterlockedDecrement | Decrement the value of a 32 bit variable. |
InterlockedExchangeAdd | Perform atomic addition on a value. |
Troubleshooting Thread Synchronization Issues
Multithreaded programming enables you to structure your software solutions based on separate code execution units for user interface interaction and background tasks. It is an advanced development technique that requires careful implementation of thread synchronization mechanisms. Deadlocks can happen, especially when using multiple synchronization objects in loops and subroutines. For example, thread One owns mutex A and waits for mutex B before releasing A, while thread Two waits for mutex A before releasing mutex B. Neither thread can continue in this situation because each depends on a resource being released by the other. These situations are hard to locate and troubleshoot, particularly when threads from multiple processes are accessing shared resources. The Remote Kernel Tracker tool identifies how threads are scheduled on the system and enables you to locate deadlocks.
The Remote Kernel Tracker tool enables you to monitor all processes, threads, thread interactions, and other system activities on a target device. This tool relies on the CeLog event-tracking system to log kernel and other system events in a file named Celog.clg in the %_FLATRELEASEDIR% directory. System events are classified by zone. The CeLog event-tracking system can be configured to focus on a specific zone for data logging.
If you have Kernel Independent Transport Layer (KITL) enabled on the target device, the Remote Kernel Tracker visualizes the CeLog data and analyzes the interactions between threads and processes. This is illustrated in Figure 3-4. While KITL sends the data directly to the Remote Kernel Tracker tool, it is also possible to analyze collected data offline.

Figure 3-4 The Remote Kernel Tracker tool
For more information about CeLog event tracking and CeLog event filtering, see the section 'CeLog Event Tracking Overview' in the Windows Embedded CE 6.0 documentation available on the Microsoft MSDN website at
Lesson Summary
Windows Embedded CE is a multithreaded operating system that provides several process management functions to create processes and threads, assign thread priorities ranging from zero through 255, suspend threads, and resume threads. The Sleep function is useful in suspending a thread for a specified period of time, but the WaitForSingleObject or WaitForMultipleObjects functions can also be used to suspend a thread until another thread or a synchronization object is signaled. Processes and threads are ended in two ways: with and without cleanup. As a general rule, always use ExitProcess and ExitThread to give the system a chance to perform cleanup. Use TerminateProcess and TerminateThread only if you have absolutely no other choice.
When working with multiple threads, it is beneficial to implement thread synchronization in order to coordinate access to shared resources within and between processes. Windows Embedded CE provides several kernel objects for this purpose, specifically critical sections, mutexes, and semaphores. Critical sections guard access to resources within a single process. Mutexes coordinate mutually exclusive access to resources shared among multiple processes. Semaphores implement concurrent access by multiple threads to resources within a process and between processes. Events are used to notify the other threads, and interlocked functions for the manipulation of variables in a thread-safe atomic way. If you happen to encounter thread synchronization problems during the development phase, such as deadlocks, use the CeLog event-tracking system and Remote Kernel Tracker