辅导EEEN30052、辅导C/C++语言、C/C++程序讲解、讲解Concurrent Systems
辅导R语言程序|解析C/C++编程
EEEN30052 Concurrent Systems 2018-19
Assignment
1. Introduction
This assignment is concerned with simulating some aspects of a simple embedded computer
system. You may choose between two versions: A1 or A2. Each is worth a different percentage
of the overall coursework mark: A1 is a simplified version of the assignment and is worth 50%,
while A2 is the full version and is worth 100%.
The diagram shown in Figure 1 is a model of a system that is responsible for gathering
environmental data from a set of three sensors, each of a different type. In the model a set of
threads (initially six) are used to gather the data, and then transmit it to a Receiver.
Access to the sensors is via a Bus Controller (BC), such that only one thread is granted access at
a time. Thus, in order to acquire data, each thread must gain sole access to the BC, configure it to
sample from a specific sensor, and sample the data. In order to transmit the data, a thread must
gain access to one of two communications links via a Link Access Controller. Having gained
access, it sends its data, and then releases the link. Each thread may use either communications
link, but once it has gained access to a particular link, no other thread should be allowed to use
that link until it is released.
Figure 12
If you choose to do A2 you should start by implementing A1, but you will not be required to
demonstrate it or upload a copy (of A1). If you choose A1 you do not need to attempt A2, or
demonstrate/upload it. However, in both cases your program should print out messages
clearly indicating the identity of the calling thread and what action it is performing.
2. A1 – worth 50% of total coursework marks
Write a C++ program that creates six threads where each thread is identified by an integer and has a
reference to an object of class BC, which is described below. Each thread executes the following sequence
50 times:
Request use of the BC by calling the requestBC() method of the BC object. This call attempts
to lock the BC to prevent access by other threads. The thread should print out its identity when the
call is successful.
Select a sensor by generating a random number between 0 and 2 inclusive, where 0 represents the
temperature sensor, 1 represents the pressure sensor, and 2 represents the capacitive sensor. Using
the number generated, request a sample value from the selected sensor by calling the BC's
getSensorValue()method. This returns a double value on the basis of the sensor that was
selected. The thread then obtains the sensor type by, for example, calling the BC's
getSensorType()method.
Print out the sensor type and value obtained, together with the thread's identity.
Increment a counter each time a particular sensor is accessed (there is a different counter for each
sensor type).
Call releaseBC() to enable other threads to use the BC, printing out its identity once more.
Delay for a random time between 1 and 10 milliseconds.
A function called run() is used to implement the above. Objects of class BC have a private boolean
member called lock to help ensure mutually exclusive access to the BC. Hence when a thread wishes to
access the BC it calls requestBC(), which in turn checks the value of lock. If the BC is not currently
in use (i.e. lock is false), the thread sets lock to true and the thread will be allowed to proceed to use
the BC. However, if the BC is in use, then the thread calling requestBC() should be suspended until the
BC has been relinquished by another thread calling releaseBC(), which in turn sets lock to false.
Thus threads are suspended –and resumed- depending on the value of lock.
Each sensor is represented by its own class (TempSensor, PressureSensor and CapacitiveSensor), which
are derived from a base abstract class called Sensor. Sensor has one virtual method called getValue(),
and one non-virtual method called getType(). Classes derived from Sensor inherit getType(), but
must implement getValue(), otherwise they are also designated 'abstract' (abstract classes can't be
instantiated). Each sensor returns values randomly chosen from within the ranges shown in Table 1.
Sensor Range of values
TempSensor 10-30
PressureSensor 95-105
CapacitiveSensor 1-5
Table 13
The main function instantiates the sensors (it is recommended to use a vector rather than an array for this
purpose), and creates six threads. It also creates one instance of class BC, passing the vector to it by
reference. It should initiate execution of the threads and should not terminate until all threads have
terminated. To help you get started the following is a partially-complete 'skeleton' of the program:
//comment: author's name, ID, and date.
//pre-processor directives:
#include
#include
....
using namespace std;
//global constants:
int const MAX_NUM_OF_THREADS = 6;
....
//global variables:
....
//function prototypes: (as required)
....
class Sensor { //abstract base class that models a sensor
public:
Sensor(string& type) //constructor
: sensorType(type) {}
//Declare a virtual method to be overridden by derived classes:
virtual double getValue() = 0;
//Declare non-virtual method:
string getType() {
//returns the type of Sensor that this is:
....
}
//Declare any instance variable(s):
....
}; //end abstract class Sensor
class TempSensor : public Sensor { //syntax declares a derived class
public:
TempSensor (string& s) //constructor
: Sensor(s) {}
virtual double getValue() {
//return a random value of ambient temperature between 10 and 30
....
} //end getValue
}; //end class TempSensor
continued..4
class PressureSensor : public Sensor {
....
....
}; //end class PressureSensor
class CapacitiveSensor : public Sensor {
....
....
}; //end class CapacitiveSensor
class BC {
public:
//constructor: initialises a vector of Sensor pointers that are
//passed in by reference:
BC(std::vector& sensors)
: theSensors(sensors) {}
void requestBC() {
....
}
double getSensorValue(int selector) {
return (*theSensors[selector]).getValue();
}
string getSensorType(int selector) {
....
}
void releaseBC() {
....
}
private:
bool lock = false; //'false' means that the BC is not locked
std::vector& theSensors; //reference to vector of Sensor pointers
std::mutex BC_mu; //mutex
....
}; //end class BC
//run function –executed by each thread:
void run(BC& theBC, int idx) {
....
for (i=0; i< NUM_OF_SAMPLES; i++) { // NUM_OF_SAMPLES = 50 (initially)
// request use of the BC:
....
// generate a random value between 0 and 2, and use it to
// select a sensor and obtain a value and the sensor's type:
....
// increment counter for sensor chosen (to keep count of
// how many times each was used)5
continued..
// release the BC:
....
// delay for random period between 0.001s – 0.01s:
....
}
}
int main() {
//declare a vector of Sensor pointers:
std::vector sensors;
//initialise each sensor and insert into the vector:
string s = "temperature sensor";
sensors.push_back(new TempSensor(s)); //push_back is a vector method.
....
// Instantiate the BC:
BC theBC(std::ref(sensors));
//instantiate and start the threads:
std::thread the_threads[MAX_NUM_OF_THREADS]; //array of threads
for (int i = 0; i < MAX_NUM_OF_THREADS; i++) {
//launch the threads:
....
}
//wait for the threads to finish:
....
cout << "All threads terminated" << endl;
//print out the number of times each sensor was accessed:
....
return 0;
}
The code is incomplete in a number of different senses, and you must modify it in order to complete A1.
You are free to include any additional methods/functions/variables/constants, as you think necessary. Note
on commenting: a solution that is poorly commented will lose up to 5% of the total marks, and one that
does not calculate the delay period correctly will also lose up to 5%.
Part of an example output is shown below:6
Notes on the Program
2.1 Thread 'Identity'
Obtaining the 'id' value of a thread in C++11 is done with
std::this_thread::get_id()
-however this returns an object not an integer, and although this can be passed to cout
for printing (e.g. cout << std::this_thread::get_id() << endl;), unfortunately there's no
simple way to cast it to an int.
For example, if you create say, three threads, and printed out their ids, you might get
2
3
4
Ideally, we want to be able to associate a known integer 'id' with each thread so we can identify them as
'thread 0', thread 1', etc..
One solution is to create a std::map of thread 'ids' v. integers, e.g.
thread 'id' int value
2 0
3 1
4 2
..etc.
To associate the thread 'id' with an integer value in a map, the following might be helpful:
std::mutex mu; //declare a mutex
std::unique_lock map_locker(mu); //lock the map via the mutex.
//insert the threadID and id into the map:
threadIDs.insert(std::make_pair(std::this_thread::get_id(), id));
map_locker.unlock(); //we're done, unlock the map.
Such code need only be executed once, and could for example, be added to the run function that each
thread executes. 7
A recommended way to search the map by thread 'id' is to use an iterator, e.g.:
std::map ::iterator it
= threadIDs.find(std::this_thread::get_id());
if (it == threadIDs.end()) return -1; //thread 'id' NOT found
else return it->second; //thread 'id' found, return the
//associated integer –note the syntax.
It might be useful to put the 'search' code in a separate function.
You will need the following preprocessor directive in order to use a map:
#include