Title: CS204 : Advanced C Prog' Threads
1CS-204 Advanced C Prog.Threads
2Motivation
- You are assigned to implement a web server which
will handle multiple user connections at the same
time, - You do not know in advance how many users will
connect to your web server - You do not know how the interaction will be
between your server and the user (some users just
connect to your server but do not interact with
it) - You need to share the CPU time equally between
the connections (your clients) - You can not block one user to get an interaction
request from another
3Motivation
- You are implementing a 3D multiplayer game, and
you need to be responsive to each player. How can
you do this ? While waiting for the first
players input, how can you let the second player
to do something ? - Or you are implementing an image processor which
has a GUI, and your program uses a heavy-weight
function which takes a considerable amount of
time to return, how can you redraw the GUI and
handle user interactions while this function does
its job ?
4Motivation
- Basically, when you are doing something that
blocks the CPU, you will need threads (which will
manage the CPU usage among multiple requests) - Similarly, if you have a big job, multiple
processors - it would be easier to divide a heavy job into
smaller ones and let each processor do some
portion of it and then combine the result.
5General Description
- Thread is a unit of execution that accomplishes a
task - A thread belongs to a process. It can not live on
its own. - Each process has at least one running thread
(primary). - Thread ? Task
- Read numbers from a file
- Display graphical user interface
- Do mathematical calculations
- ...
- At each instant, the CPU is addressing only one
thread, but by rotating among threads, it gives
the feeling that each thread is handled
simultaneously
6Thread vs. Process
- Both threads and processes are methods of
parallelizing an application. - However, processes are independent execution
units that contain their own state information,
use their own address spaces, and only interact
with each other via interprocess communication
mechanisms. - By contrast, a thread is a coding construct. A
single process might contain multiple threads
all threads within a process share the same state
and same memory space, and can communicate with
each other directly, because they share the same
variables
7Thread vs. Process
- Per Process Items
- Address Space
- Global Variables
- Open Files
- Child Processes
- Signals
- Accounting Info.
- Per Thread Items
- Program Counter
- Registers
- Stack
- Thread State
- Signals
8Thread vs. Process
- Each process provides the resources needed to
execute a program. A process has, - A virtual address space,
- Executable code,
- Open handles to system objects,
- A unique process identifier,
- Environment variables,
- A priority class,
- And at least one thread of execution.
- Each process is started with a single thread,
often called the primary thread, but can create
additional threads from any of its threads.
9Thread vs. Process
- A thread is the entity within a process that can
be scheduled for execution. - All threads of a process share its virtual
address space and system resources. - Each thread maintains
- Exception handlers,
- A scheduling priority,
- Thread local storage,
- A unique thread identifier,
- And a set of structures the system will use to
save the thread context until it is scheduled
(thread's set of machine registers, the kernel
stack, a thread environment block, and a user
stack in the address space of the thread's
process)
10Process Spawning
- We saw that processes can be spawned (executed)
to parallelize the application, Win32 and MFC
provides proper methods and functions to
accomplish this - CreateProcess
- ShellExecute
- Demo code ...
- But what happens if we want to do communication
and share data between processes ? Why ? - IPC (Inter Process Communication)
- Sockets, Pipes, Shared Memory
- What about management of the processes ?
- Killed by the system or killed by the user
11Single vs. Multi Threads
12Threads
- Todays many operating systems (Windows, Linux,
BSD, Solaris) support the multi-threading on
various levels - Kernel Level
- User Level
- Hybrid
- We will cover user level threads
13Threads in Windows (MFC)
- Thread Operations
- Creating Threads
- Suspending/Resuming Threads
- Terminating/Exiting Threads
- Synchronizing Execution of Multiple Threads
14Threads in MFC
- MFC distinguishes two types of threads
- User-interface threads and worker threads.
- User-interface threads are commonly used to
handle user input and respond to events and
messages generated by the user. - Worker threads are commonly used to complete
tasks, such as recalculation, heavy-weight
functions that do not require user input.
15Worker Threads
- A worker thread is commonly used to handle
background tasks that the user should not have to
wait for to continue using your application. - Tasks such as
- Heavy-weight mathematical calculations
- Background printing
- Creating a worker thread is a relatively simple.
Only two steps are required to get your thread
running - Implementing the controlling function
- Starting the thread.
16Controlling Function
- The controlling function defines the thread. When
this function is entered, the thread starts, and
when it exits, the thread terminates. This
function should have the following prototype - UINT AnyControllingFunction ( LPVOID pParam )
17SOME TYPES DEFINED IN WIN API
- typedef int BOOL
- typedef unsigned int UINT
- typedef int INT
- typedef char CHAR
- typedef unsigned char BYTE
- typedef void VOID
- typedef float FLOAT
- typedef FLOAT PFLOAT //pointer to
float - typedef void LPVOID //pointer to void
- typedef CHAR LPSTR
- typedef const CHAR LPCSTR
- typedef void PVOID
- typedef PVOID HANDLE
- typedef HANDLE HINSTANCE
- typedef HANDLE HLOCAL
- typedef char PSTR
- ...
18Controlling Function
- UINT AnyControllingFunction ( LPVOID pParam )
- The parameter is a single value.
- It was passed to the constructor when the thread
object was created. - The controlling function can interpret this value
in any manner it chooses. It can be treated as - A scalar value
- A pointer to a structure containing multiple
parameters - It can be ignored.
- If the parameter refers to a structure, the
structure can be used not only to pass data from
the caller to the thread, but also to pass data
back from the thread to the caller.
19Starting Worker Threads
- You can derive a class if you need a special
version of CWinThread, but it is not required for
most simple worker threads. You can use
CWinThread without modification - To start your worker thread call,
- AfxBeginThread with providing the following
information - The address of the controlling function
- The parameter to be passed to the controlling
function - pNewObject new MyObject
- AfxBeginThread(MyControlFunc, pNewObject)
CWinThread AfxBeginThread( AFX_THREADPROC
pfnThreadProc, LPVOID pParam, int nPriority
THREAD_PRIORITY_NORMAL, UINT nStackSize
0, DWORD dwCreateFlags 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs NULL )
20Worker Thread Example
- include "stdafx.h"
- include ltiostreamgt
- using stdcout
- using stdendl
- UINT MyThreadFunc1 (LPVOID param)
-
- while (true)
- cout ltlt "Worker with no param" ltlt endl
- Sleep (500)
-
- return 0
-
- UINT MyThreadFunc2 (LPVOID param)
-
- int incoming (int) param
- while (true)
21Worker Thread Example
- int main()
-
- int parameter 1000
- AfxBeginThread (MyThreadFunc1, NULL)
- AfxBeginThread (MyThreadFunc2, parameter)
- WaitForSingleObject(GetCurrentThread(),
INFINITE) - return 0
-
22Suspending and Resuming Thread Execution
- Suspending a thread blocks the execution of the
thread, when it is resumed it starts the
execution from the point it is suspended - AfxBeginThread returns a pointer to CWinThread
object, we can use that pointer to suspend and
resume the thread - CWinThread curThread AfxBeginThread
(MyThreadFunc, NULL) - .....
- curThread-gtSuspendThread()
- ....
- curThread-gtResumeThread()
- Sleep suspends the execution of the thread for a
given amount of time - Sleep (100) // Suspends 100 milliseconds
23User Interface Threads
- User-interface threads have a message pump and
process messages received from the system, - If we do not need to process any system messages
worker threads are fine - Just implement a controlling function
- And pass that function to AfxBeginThread
- But if we need to process system messages such
as, - User inputs
- User interactions
- Other OS messages (data on socket, and etc.)
- We need to use User Interface Threads
24User Interface Threads
- For GUI applications the main thread of execution
is provided by an object derived from CWinApp.
CWinApp is derived from CWinThread. - Additional CWinThread objects allow multiple
threads within a given application, - Basically our GUI applications are derived from
CWinApp which constructs the primary thread of
our application - class CThreadDemoApp public CWinApp
-
- .....
25User Interface Threads
- In order to implement a multithreaded application
we need to implement a class (or classes) which
will derive from CWinThread - class CSocketThread public CWinThread
-
- .....
-
- We need to override the following methods
- virtual BOOL InitInstance( )
- Override to perform thread instance
initialization. - virtual int ExitInstance( )
- Override to clean up when your thread terminates.
- virtual int Run( )
- Controlling function for threads with a message
pump. Override to customize the default message
loop.
26User Interface Threads
- To create the thread we will use CreateThread
method. This comes for free when we derive from
CWinThread class. - When CreateThread is called from our application,
CWinThread will call our derived InitInstance
method and will leave the execution to our
derived Run method
27CWinThread Demo
- class CSocketThread public CWinThread
-
- public
- CSocketThread(int pSocketId)m_SocketId(pSocketId
) - virtual int Run()
- virtual int ExitInstance()
- virtual BOOL InitInstance()
- private
- int m_SocketId
28CWinThread Demo
- ...
- int CSocketThreadRun()
-
- while (true)
-
- cout ltlt "Thread with Socket Id" ltlt m_SocketId
ltlt endl - Sleep (1000)
-
- return 0
-
- ...
- int main()
-
- CSocketThread firstThread(10)
- CSocketThread secondThread(20)
- firstThread.CreateThread()
29Thread Synchronization
- Until now, we created threads and let them run.
They do not share anything. - But suppose, you have a global data and some of
the threads are reading that data and some of
them are writing into it. - What happens when they access it at the same time
? - Hint what happened when both threads accessed the
command line window to print out some info ? - (Undesirable and unpredictable results)
30Thread Synchronization
- Synchronizing resource access between threads is
a common problem when writing multithreaded
applications. - Having two or more threads simultaneously access
the same data can lead to undesirable and
unpredictable results. - For example, one thread could be updating the
contents of a structure while another thread is
reading the contents of the same structure. It is
unknown what data the reading thread will
receive the old data, the newly written data, or
possibly a mixture of both. - MFC provides a number of synchronization and
synchronization access classes to aid in solving
this problem.
31Thread Synchronization
- Mutexes, Locks, Semaphores, and CriticalSections
are used to prevent the data - CMutex
- CSingleLock, CMultipleLock
- CSemaphore
- CCriticalSection
- Basic idea here is to give the access of the data
to only one thread at a time (which is also
called thread-safe) - We will cover only one method shown above
(CMutex) - The details of the MFC synchronization classes
can be found on MSDN, but difference is the
low-level structs used by MFC library (Faster or
Slower thread context switches)
32Thread Synchronization Demo
- class CSynchThread public CWinThread
-
- public
- CSynchThread(CMutex pLock, int pSocketId)
- ...
- private
- int m_SocketId
- CMutex m_Lock
-
- ...
- CSynchThreadCSynchThread(CMutex pLock, int
pSocketId) - m_SocketId(pSocketId), m_Lock(pLock)
-
33Thread Synchronization Demo
- int CSynchThreadRun()
-
- while (true)
- m_Lock-gtLock()
- cout ltlt "Thread with Socket Id" ltlt m_SocketId
ltlt endl - m_Lock-gtUnlock()
- Sleep (1000)
-
- return 0
-
- int main()
- CMutex mutex new CMutex()
- CSynchThread firstThread(mutex, 10)
- CSynchThread secondThread(mutex, 20)
- firstThread.CreateThread()
34What can go wrong ?
- Debugging a multi-threaded application needs much
more effort. - Too many switches between threads degrades the
performance - Programmer needs to pay attention to problems
such as, - Synchronization, Deadlocks, Starvation, and Race
Conditions - Most of the data types and classes available for
use are not thread safe
35Deadlocks
- Deadlocks occur when two or more threads (or
processes) wait for each other to release a
resource or waiting for resources in a circular
chain, - Thread -1 Thread-2
- LockResource(A) LockResource(A)
- DoActionOn(A) DoActionOn(A)
- ReleaseResource(A) //Forgetting to release
36Deadlocks
- Waiting for resources in circular chain,
- Resources (A and B)
- Threads (T1 and T2)
Resource-A is assigned to T2 and T2 waits for
Resource-B to accomplish its job and release
resources Resource-B is assigned to T1 and T1
waits for Resource-A to accomplish its job and
release resources
A
B
- There are algorithms to detect, avoid and prevent
deadlocks (Will be mentioned in OS Course)
37Starvation
- Starvation (or Resource Starvation)
- From two or more of the threads, one thread is
scheduled more often to execute but others are
not - Faulty scheduling
- One of the threads is unwilling to give up the
resources for which the other threads are waiting - Famous Dining Philosophers Problem
38Race Conditions
- Race conditions arise when separate processes or
threads depend on some shared state. - Example
- Let us assume that two threads T1 and T2 each
want to increment the value of a global integer
by one. - Integer i 0
- T1 reads the value of i from memory into a
register 0 - T1 increments the value of i in the register
(register contents) 1 1 - T1 stores the value of the register in memory 1
- T2 reads the value of i from memory into a
register 1 - T2 increments the value of i in the register
(register contents) 1 2 - T2 stores the value of the register in memory 2
- Integer i 2
- In the case shown above, the final value of i is
2, as expected. - However, if the two threads run simultaneously
without locking or synchronization, the outcome
of the operation could be wrong.
39Race Conditions
- Integer i 0
- T1 reads the value of i from memory into a
register 0 - T2 reads the value of i from memory into a
register 0 - T1 increments the value of i in the register
(register contents) 1 1 - T2 increments the value of i in the register
(register contents) 1 1 - T1 stores the value of the register in memory 1
- T2 stores the value of the register in memory 1
- Integer i 1
- The final value of i is 1 instead of the expected
result of 2. - This occurs because the increment operations of
the second case are non-atomic. Atomic operations
are those that cannot be interrupted while
accessing some resource, such as a memory
location.
40Threads for Windows
- MFC Threads are not the only solution for
threading on Windows, - POSIX Threads (or PThreads) Library
- http//sourceware.org/pthreads-win32/
- OpenThreads Library
- http//openthreads.sourceforge.net/
- Win32 Threads
- http//msdn2.microsoft.com/en-us/library/
-