win32 programming lesson 9: jobs & thread basics
TRANSCRIPT
Win32 ProgrammingLesson 9: Jobs & Thread Basics
Where are we? We’ve got processes sort of worked out… But every process must have a thread
associated with it This lesson, we’ll work on threads –
understanding threads is critical to understanding Windows programming
Also, a little bit on jobs…
What is a Job A collection of processes For example, imagine interrupting the build in
Visual Studio Windows 2000 offered a new kernel object to
allow this
Basic Idea Create a Job Kernel Object
CreateJobObject Place restrictions on the job object
SetInformationJobObject Start up some processes…
CreateProcess But, set CREATE_SUSPENDED flag Assign processes to job Start the primary thread of each process
Termination You can stop all processes in a job simply by
terminating the Job Object TerminateJobObject(HANDLE hJob, UInt
uExitCode)
Threads Each thread is made up of two objects
A kernel object used by the Operating System to manage the thread
A thread stack that maintains local variables and function parameters as the thread executes
Threads v. Processes Processes take up a lot more system resources
than threads Processes are inert – they are simply a
container for one or more threads Always solve a problem by adding threads
not processes if you possibly can!
When to Create Threads Many applications only have one thread The process terminates when the primary
thread finishes However, processes can have as many threads
are you like… and there’s no reason for the CPU to be idle (unless you’re on a laptop!) Example: Web browsers have separate threads for
IO so the UI remains responsive
When Not to Create Threads Some things really do need to happen in
sequence Word processor, with its own thread for
printing… why not? UI’s – have one GetMessage loop, with multiple
worker threads Moral: Don’t use multiple threads just
because you can
Creating Threads Once you’ve got one thread running (i.e.
you’ve executed your program) you can start more
See MSDN for an example application Based upon a thread function which gets
called, of form: DWORD WINAPI ThreadFunc(PVOID
pvParam)
Caveat Emptor Use Local variables in threads wherever you
can – static and global variables can be modified at any time by other threads, leading to all sorts of interesting race conditions
Your thread function must return a DWORD value
CreateThread HANDLE CreateThread(
PSECURITY_ATTRIBUTES psa, DWORD cbStack,
PTHREAD_START_ROUTINE pfnStartAddr,
PVOID pvParam, DWORD fdwCreate, PDWORD pdwThreadID);
Parms psa: Security attributes – usually NULL if you
want the default cbStack: The amount of stack space to
reserve; 0 gets you the default pfnStartAddr: Pointer to the function to call to
start the thread pvParam: Pointer to any parameters you wish
to pass
Bugs DWORD WINAPI FirstThread(PVOID pvParam) {
// Initialize a stack-based variable int x = 0; DWORD dwThreadID; // Create a new thread. HANDLE hThread = CreateThread(NULL, 0, SecondThread, (PVOID) &x,
0, &dwThreadId); // We don't reference the new thread anymore, // so close our handle to it. CloseHandle(hThread); // Our thread is done. // BUG: our stack will be destroyed, but // SecondThread might try to access it. return(0);
} DWORD WINAPI SecondThread(PVOID pvParam) {
// Do some lengthy processing here. // Attempt to access the variable on FirstThread's stack. // NOTE: This may cause an access violation _ it depends on timing! * ((int *) pvParam) = 5; return(0);
}
How to Solve this Problem Hokey: create a static variable so the memory is
allocated away from the thread stack Better: use proper thread synchronization techniques
– but that’s another story
Parms (cntd) fdwCreate: 0 (get on with it) and
CREATE_SUSPENDED (create it paused). See JobLab example for how to use this flag
pdwThreadID: the address of a DWORD in which CreateThread stores the ID assigned to the new thread
Terminating a Thread Four ways:
The thread function returns (good) The thread kills itself by calling ExitThread (bad) Another thread calls TerminateThread (bad) The process containing the thread terminates (bad)
Can use function to get exit code: BOOL GetExitCodeThread(
HANDLE hThread, PDWORD pdwExitCode);
Thread Startup
Thread Context Each thread has its own set of CPU registers Saved in a CONTEXT structure contained in
the thread’s kernel object IP and SP are the most important BaseThreadStart is called internally by the OS
BaseThreadStart Sets up default SEH System calls the function pointed to in
CreateThread, passing in pvParam On Thread exit, return return code If an exception is caught, handle it; this
involves terminating the entire process not just the offending thread
C/C++ Considerations Beware: You’re reading about how the OS
handles threads. Missing logical “sugar” when considering the C/C++ RTLs
Read the MSDN section on threads – it’s very useful!
Also: _beginthreadex
A Sense of Self Threads can learn about themselves via:
HANDLE GetCurrentProcess() HANDLE GetCurrentThread() Return pseudo-handles not true unique identifiers See also GetProcessTimes and GetThreadTimes Can use DuplicateHandle to get a real handle to
the thread