220 likes | 474 Views
Win32 Programming. Lesson 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
E N D
Win32 Programming Lesson 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 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