Win32 Programming

Document Sample
Win32 Programming Powered By Docstoc
					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
   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
   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
   Can gain efficiency as threads are re-used, not
    recreated for each new work item (system
    spends less time creating and destroying
   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
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
           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
   Can pass NULL to the timer for
       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
   BOOL RegisterWaitForSingleObject(
       PHANDLE phNewWaitObject,
       HANDLE hObject,
       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
Function Format
   BOOL BindIoCompletionCallback(
       HANDLE hDevice,
       ULONG dwFlags
   Callback function of form:
       VOID WINAPI OverlappedCompletionRoutine(
           DWORD dwErrorCode,
           DWORD dwNumberOfBytesTransferred,
           POVERLAPPED pOverlapped
   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
   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
       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

Shared By: