Professional Documents
Culture Documents
04 - Principles of Concurrent Systems - Threads PDF
04 - Principles of Concurrent Systems - Threads PDF
Systems - Threads
◼ In the last lecture we saw how it was possible, using the CProcess() class, to
design and code a system composed of a number of smaller processes that
could be executed in parallel or concurrently instead of one large monolithic
single tasking application.
◼ The term ‘granularity’ originates from the idea that any big rock (our
monolithic single tasking system) could be broken down into smaller and
smaller rocks (starting with process decomposition).
◼ (See http://en.wikipedia.org/wiki/Granularity#In_computing)
3
Thread Granularity
◼ In addition to breaking a system down into a number of concurrent
processes, we could break down each process further into a number of
parallel executing ‘threads’ each of which represents a traceable path of
sequential programming within a process.
◼ In theory we could use threads to decompose our system into finer and
finer sections of parallel executing code until we achieve perfect or fine
grained granularity where almost everything is run in parallel. We saw last
week when we discussed how to calculate the expression B2 – 4AC)
Process
Process Process
Process Process
Process
System broken down
Processes broken into processes:
down into smaller Coarse grained or
processes Process Granularity
Process
Process Process
Process Process
Process
Smaller Processes
Multi-threaded broken down into
systems threads
Processes broken down
Thread
Thread Thread
Thread Thread
Thread into threads: Fine
grained or Thread
Granularity
Threads broken down
into smaller threads
Process/Thread
Thread
Process/Thread
Thread Process/Thread
Thread
Communication Problems Synchronisation Problems
Process/Thread
Thread Process/Thread
Thread
Process/Thread
Thread
Race Problems in Concurrent Systems Design 6
Process A Process B
Line A1: x = 5; Line B1: x = 7;
Line A2: print x;
◼ It depends on the order in which the statements are executed at run time.
◼ One possible execution order is [Lines: A1 -> A2 -> B1], in which case the
printout of the program is 5, but the final value of x = 7.
Puzzle: What execution order yields a printout of 5 and a final value for x = 5?
Puzzle: What execution order yields a printout of 7 and a final value for x = 7?
Puzzle: Is it possible to see a printout of 7 and a final value for x = 5? Are you sure?
Puzzle: Will we always see the same result each time we run these two program. What factors
may influence this?
◼ Every application/program that you write has at least one thread that starts
and ends with the function main().
◼ All the programs you wrote in CPSC 260/259 were single threaded as they
had a single point of entry and exit marked by main().
Scheduling of Threads
◼ This is handled by the OS. Kernel using
◼ Responsiveness
◼ A multi-threaded application does not have to wait for one activity to
That is, there is less information to save during a task swap between
threads in the same program, compared to threads within different
programs, so a multi threaded program is more efficient than a program
broken down only into multi-processes.
Creating Threads
using the RT Library
-
The CThread Class
◼ The RT Library creates threads by making direct calls to the Windows Kernel. This 12
requires that any parameters passed to the thread are passed as a single reference (by
pointer). The thread function also has a slightly peculiar signature in Visual C++.
◼ A CThread object is needed to represent and control each thread within your process.
The constructor for this class will invoke the necessary Kernel calls to create and
schedule a thread to run on the computer.
Special Microsoft specific Thread Signature
void main() Pointer to Optional data we can pass to the thread at creation
{
CThread t1( ChildThread1, ACTIVE, NULL) ; Process’s Main Thread
...
t1.WaitForThread() ; // if thread already dead, then proceed, otherwise wait
}
Application/Process Source File with 2 Threads
A More Detailed Example Program using Multiple Threads (See Q2 for Example) 13
#include "rt.h”
Child Thread 1
UINT _ _stdcall ChildThread1( void *args )
{
for (int i = 0; i < 1000; i ++)
cout << "Hello From Thread 1\n” ;
return 0 ;
}
Child Thread 2
UINT _ _stdcall ChildThread2( void *args )
{
for (int i = 0; i < 1000; i ++)
cout << "Hello From Thread 2\n” ;;
return 0 ;
}
t1.WaitForThread() ; // if thread already dead, then proceed, otherwise wait for it to finish
t2.WaitForThread() ; // if thread already dead, then proceed, otherwise wait for it to finish
}
◼ These member functions are outlined below with a brief description of what they do.
They are similar to those of the CProcess class.
◼ A more detailed description and implementation of them can be found in the rt.h and
rt.cpp files.
#include "rt.h"
We can pass information to the thread using this parameter
return 0 ;
}
Code for a Child Thread
// Now here is the clever bit with threads. Let's create 8 instances of the above
// single thread function and let each thread know who it is (i.e. give it a number).
// Such data can influence it’s behaviour
// wait for threads to terminate, then delete thread objects we created above
To wait for a thread to terminate, call the windows kernel function WaitForSingleObject().
Using the handle of the thread created above and a specified time to wait.
Thread Local Storage 18
◼ Thread aware compilers support variables that can be instantiated once for each thread.
◼ In Visual C++ this is achieved by prefixing variables with the key word __declspec(thread).
◼ In the RT library, this has been #defined as PerThreadStorage
PerThreadStorage int MyThreadNumber ; Instance of this variable created for each thread
Select a `pivot' value from the array (usually the value of the middle element)
Partition the array into two smaller left and right arrays, such that
All elements in the left array have a value less than or equal to the
pivot value
All elements in the right array have a value greater than or equal to the
pivot value
QuickSort ( Left Array, size of Left Array )
QuickSort ( Right Array, size of Right Array )
20
◼ Homework Problem: Discuss this algorithm in terms of it’s efficiency and speculate
under what circumstances you would or would not see significant improvements in its
execution speed in practice – Some factor to consider include the effects of hardware
platform (e.g. number of CPUs/cores), the size of data being sorted and the time of an
OS to create and swap between threads.
Concurrent Programming in an Object Oriented World 21
◼ In languages like Java we can create multiple threads within our processes
through the elegant use of ‘active objects’, i.e. objects with their own function
run() that have their own thread of execution running through them.
◼ Such objects execute their main() concurrently with all other ‘active’ objects in
the system and concurrently with the process main() thread.
◼ The objects main() (itself a thread), could create other child threads within the
active object. In a sense, an active object is like a complete mini application,
with a main(), functions, variables and child threads.
◼ An active object is an object with a brain. Imagine if the CBulb class from lab 1
were an active object. It could decide to do things for itself (from within its
main()). It could turn itself on an off when it decided to. This in unlike a passive
object which would only respond when code outside the object invoked one of
its on() or off() functions.
◼ In Visual C++ (using the RT library and Windows Kernel) we can create new
active classes by deriving from a base class ActiveClass as shown on next slide.
(See also Q2A and Q2B tutorials for an example)
Active Classes in the RT Library
◼ We override the function main() inherited from that base class 'ActiveClass' to do
whatever we want our class object to do, i.e. we provide a private function main() then
we create instances of the class to create threads.
◼ These threads are controlled via the member functions of the CThread class as shown
previously. Note that such threads are created in a suspended state so they have to be
resumed later with a call to the Resume() function.
class MyActiveClass : public ActiveClass {
… A Class derived from ActiveClass
private:
return 0 ;
} Derived class main thread. Note ‘.h’ and ‘.cpp’ files
}; have been combined to illustrate concept
class MyActiveClass1 : public ActiveClass { Combined class .cpp and .h file 23
private:
int main( void ) { // a thread within my class
for (int i = 0; i < 1000; i ++)
cout << "Say Hello to My Active Class 1.....\n";
return 0 ;
}
}; Active Class objects have a brain and a thread running inside their main()
return 0 ;
}
}; Active Class objects have a brain and a thread running inside their main()
int main(void)
{
MyActiveClass1 object1, object2, object3 ; // create 3 instances of the above class
MyActiveClass2 object4, object5, object6 ; // create 3 instances of the above class
object1.WaitForThread() ;
object2.WaitForThread() ;
…..
// wait for the above two child threads of the class to terminate No thread data
Supplemental Notes
-
Examples of Creating
Threads with C++ 11 and
other Libraries.
27
Creating Threads with C++ 11 for Windows
◼ New header file <thread> to include
◼ Signature of thread function accept any parameters
◼ To control thread still needs call the Windows Kernel
#include <iostream>
#include <thread> Include “thread” header file
int ChildThread( int x ) // A thread function taking, in this example an int parameter
{
for (int i = 0; i < 100; i++)
cout << "Child thread [" << x << "] \n";
return 0;
}
◼ Code still has to compile to call the Windows Kernel so how do you think the
compiler translates the parameters given that native windows call requires a
“void pointer” to the threads parameters?
int main() 28
// wait for threads to terminate, then delete thread objects we created above
for ( int j = 0; j < 8; j++) {
Threads[j]->join(); // wait for each thread to die
delete Threads[j]; // delete the object created by ‘new’
}
return 0;
}
Summary: Use thread instead of CThread, join() instead of WaitForThread(). More flexibility w.r.t. to
passing arguments to the thread, but no C++ standardisation related to controlling the thread, e.g.
suspend, resume, kill etc. See here https://en.cppreference.com/w/cpp/thread/thread. You can get
the thread id/handle and then call the kernel directly if required
Creating threads with UNIX/Linux POSIX Library 29
◼ New threads are created using pthread_create() and we wait for them to
terminate using pthread_join(). Technique is used in OSX and Android.
Child Thread
void main(void)
{
…
OSTaskCreate( Task1, OS_NULL, &Task1Stk[STACKSIZE], 12) ;
OSTaskCreate( Task2, OS_NULL, &Task2Stk[STACKSIZE], 11) ;
…
}