Win32 Programming
Document Sample


Win32 Programming
Lesson 13: Thread Pooling
(Wow, Java is good for something…)
Where are we?
We know everything there is to know about
threads in Windows
Not
But there’s only another 2 lectures on this
Introduce a simplifying idea: thread pooling
What is Thread Pooling
Thread pooling allows us to let the OS create and
destroy threads for us
Each worker thread gets woken up when there’s a
job to do
The OS decides whether to create a new thread or
wait for an existing one
Caveat emptor: setting up a thread pool is quite
expensive – the wins come on continued reuse
Scenario 1: Call functions
asynchronously
Very common problem
Example: a server which spawns a worker
thread to serve a particular client
In this case, the function
QueueUserWorkItem is quite handy
QueueUserWorkItem
BOOL QueueUserWorkItem(
PTHREAD_START_ROUTINE pfnCallback,
PVOID pvContext,
ULONG dwFlags
);
Queues a thread work item and returns immediately
Callback function must have the form:
DWORD WINAPI WorkItemFunc(PVOID pvContext);
Note the return code is ignored
What do you Notice?
No call to _beginthreadex or CreateThread
The system creates and manages threads for
you
Can gain efficiency as threads are re-used, not
recreated for each new work item (system
spends less time creating and destroying
threads)
Beware
Because the system is managing the threads,
you need to be very careful if you put your
thread to sleep and wait for an asynchronous
request (like IO)
Set dwFlags to WT_EXECUTEINIOTHREAD
Need to help the system decide that a thread may
take a long time to complete (so set
WT_EXECUTELONGFUNCTION)
Call Functions at Timed Intervals
Create a queue:
HANDLE CreateTimerQueue();
Create timers in the queue:
BOOL CreateTimerQueueTimer(
PHANDLE phNewTimer,
HANDLE hTimerQueue,
WAITORTIMERCALLBACK pfnCallback,
PVOID pvContext,
DWORD dwDueTime,
DWORD dwPeriod,
ULONG dwFlags
);
Timer work function
Must be of form:
VOID WINAPI WaitOrTimerCallback(
PVOID pvContext,
BOOL fTimerOrWaitFired
);
fTimerOrWaitFired is always TRUE
WT_EXECUTEINTIMERTHREAD is
interesting
Executes in timer thread
Very dangerous, but useful when used correctly
Deleting Timers
Must do this even for one-shot timers
BOOL DeleteTimerQueueTimer(
HANDLE hTimerQueue,
HANDLE hTimer,
HANDLE hCompletionEvent
);
Blocks until completion if hCompletionEvent is
INVALID_HANDLE_VALUE
But…
Can pass NULL to the timer for
hCompletionEvent
In this case, the Timer is deleted ASAP, but you
won’t know when
Finally, can pass an Event Kernel Object
Sets event when the timer is actually deleted
Scenario 3: KO Signaled
Lots of applications spawn a thread to wait on
completion of a Kernel Object
Wasteful of resources – if this happens a lot better to
use a thread pool
CPU Intensive operation when you create/destroy threads
Still better than a process
API
BOOL RegisterWaitForSingleObject(
PHANDLE phNewWaitObject,
HANDLE hObject,
WAITORTIMERCALLBACK pfnCallback,
PVOID pvContext,
ULONG dwMilliseconds,
ULONG dwFlags
);
hObject is the object to wait on
Time is between 0 and INFINITE
Unregistering a Wait
If the desired object gets signaled multiple times, the
Wait object will be woken up multiple times
Unless of course you set WT_EXECUTEONLYONCE
But, you must still call:
BOOL UnregisterWaitEx(
HANDLE hWaitHandle,
HANDLE hCompletionEvent
);
Call Functions on I/O
Common scenario
Wish to call a function when an
asynchronous I/O event completes
You may want to look up “I/O Completion
Ports”
Function Format
BOOL BindIoCompletionCallback(
HANDLE hDevice,
POVERLAPPED_COMPLETION_ROUTINE pf
nCallback,
ULONG dwFlags
);
Callback function of form:
VOID WINAPI OverlappedCompletionRoutine(
DWORD dwErrorCode,
DWORD dwNumberOfBytesTransferred,
POVERLAPPED pOverlapped
);
Fibers
The idea came when MS thought about
application developers porting applications
from UNIX to Windows
UNIX lacks the same threading functionality
that Windows has, and so implements
“threading” quite differently
Fibers make port a lot easier… but they are no
substitute for native Windows threads
A what?
Every thread contains one or more fibers
As far as the Kernel is concerned, each thread
gets preemptively scheduled
The thread decides which fiber to execute
Only one fiber gets executed at a time
API
First, must convert the existing thread to a Fiber:
PVOID ConvertThreadToFiber(PVOID pvParam);
This function allocates memory (about 200 bytes) for the
fiber's execution context which contains:
A user-defined value that is initialized to the value passed to
ConvertThreadToFiber's pvParam argument
The head of a structured exception handling chain
The top and bottom memory addresses of the fiber's stack (When
you convert a thread to a fiber, this is also the thread's stack.)
Various CPU registers, including a stack pointer, an instruction
pointer, and others
Create Additional Fibers
No point ever converting a thread if you don’t
do this!
PVOID CreateFiber(
DWORD dwStackSize,
PFIBER_START_ROUTINE pfnStartAddress,
PVOID pvParam
);
Function API is:
VOID WINAPI FiberFunc(PVOID pvParam);
Executing Fibers
To make the new fiber execute you call:
VOID SwitchToFiber(PVOID pvFiberExecution
Context);
This stores needed information on the current
fiber and sends processing to the new one
It’s the only way a fiber can get CPU time
Destroy using DeleteFiber
Get documents about "