代写EEEN30052作业、代写C/C++语言作业、C/C++程序作业代做、代做Concurrent Systems作业

代写EEEN30052作业、代写C/C++语言作业、C/C++程序作业代做、代做Concurrent Systems作业
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 1
2
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 1
3
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 <iostream>
#include <thread>
....
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<Sensor*>& 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<Sensor*>& 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<Sensor*> 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<std::mutex> 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 <std::thread::id, int>::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 <map>
For more info about maps, with more examples, see for example:
http://thispointer.com/stdmap-tutorial-part-1-usage-detail-with-examples/
2.2 Creating a delay by generating random values.
One way to generate say, random integers, is to use a pseudo-random number generator (prng), and 'seed'
it with a value. The code below shows one way of doing this (adapted from
http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution)
std::mt19937 gen(time(0)); //A prng seeded by time(0).
//time(0) gives the number of seconds elapsed since the start
//of the world according to Unix (00:00:00 on 1st January 1970).
std::uniform_int_distribution<> dis(1, 1000); //set up the distribution
//from which integers will be drawn.
int n = dis(gen); //generate a pseudo-random integer between 1-1000.
-the pseudo-random number is stored in n.
You will need the following preprocessor directives in order to use the above example code:
#include <random>
#include <chrono>
Note that only a single generator is necessary, which only needs to be initialised once. Furthermore, a
solution that does not calculate the delay period correctly will lose up to 5% of the total marks.
2.3 Vectors or Arrays?
A vector and an array are similar in that they both contain a list of data, however the basic difference is
that the size of an array is fixed, whereas a vector 'grows' automatically as more items are added to it.
Furthermore, the availability of methods associated with vectors make them very flexible.
In general in C++11 it is straightforward to create arrays of objects of classes from the standard library,
such as std::thread, for example. However, arrays of objects of user-defined classes, such as
Sensor, or Link, are more awkward to use. For such user-defined classes you may find
std::vector much easier to handle, for example:
std::vector<Sensor*> sensors; //vector of pointers to Sensor objects
or std::vector<Link> commsLinks; //vector of Link objects
A example tutorial is:
http://www.codeguru.com/cpp/cpp/cpp_mfc/stl/article.php/c4027/C-Tutorial-A-Beginners-Guide-tostdvector-Part-1.htm
8
2.4 Inheritance
In C++ one class (the 'base' class) can define methods and variables/constants (i.e. class 'members') which
can be inherited by another ('derived' class). In principle this means that such methods/variables/constants
need to be defined only once (thereby promoting re-useability) yet can be used by instances of both base
and derived classes. In the following example, B declares its own members, in addition to those inherited
from A:
#include <iostream>
using namespace std;
class A{ //base class
public:
A (int i) //constructor
: x(i) { } //initialise private instance variable x
void set_x (int a) { x = a; } //set value of x
int get_x ( ) { return x; }
private:
int x; //instance variable
}; //end class A
class B : public A { //derived class
public:
B (int i, int j) //constructor must call A's constructor
: A(i) {p = j;} //initialise private instance variable p
void set_p(int a) { p = a; } //set value of p
int get_p( ) { return p; }
private:
int p; //instance variable
}; //end class B
//The following illustrates use:
int main() {
A a(3); //create instance of A, initialise member variable
//x to 3
a.set_x(4); //change x to new value
cout << a.get_x() << endl; //and print it out
B b(5,4); //create instance of B and initialise member
//variables x to 5 and p to 4
b.set_x(6);//use b uses inherited method to set new value of x
cout << b.get_x() << endl; // use b to print out x
cout << b.get_p() << endl; //and print out p
}
prints out
4
6
4
If a class contains one or more methods that are declared 'virtual' then the class is said to be 'abstract' and
can't be instantiated; however any derived classes from which instances are required are then obliged to
define the method. This is useful if, for some reason it didn't make sense to define the method in the base
class, yet you still wanted derived classes to have it, but define it in different ways. Using the above as an
example, if in class A we had declared set_x() as a virtual method, i.e.
virtual void set_x (int a) = 0;//syntax specifies method as virtual
then any attempt to instantiate A now fails:
9
A a(3); //FAILS –can't make an instance of abstract class A
Because B doesn't implement set_x() the following also fails to compile:
B b(5,4); //FAILS –can't make an instance of B
In order for this statement to compile, B must implement set_x(), for example by writing
void set_x (int a) { x = a; } //set value of x
Inheritance is a feature of object-oriented software design that permits the use of powerful techniques,
such as polymorphism. The following link provides more information:
http://www.cplusplus.com/doc/tutorial/inheritance/
3. A2 – worth 100% of total coursework marks
In A1 we were concerned with creating threads and being able to lock access to the Bus Controller. In this
version we extend the solution to include access to the communications links. In particular, the threads
should request a Link from a LinkAccessController object. When one is available, the sample
values that the thread has acquired can be transmitted. The Link object is used to copy this data to the
Receiver object. To facilitate this, each thread stores sensor data in objects of a class called
SensorData, which stores the samples internally in a vector.
The main program should create and initiate all the threads, and should wait until they have all have
terminated before calling the Receiver’s method that prints out all the sensor data.
Part A2 builds on A1 and requires the following additional classes:
SensorData. As indicated above, data sampled by each thread is stored in a vector managed by
instances of this class. Once started, each thread would create three instances, one for each sensor type.
When a sample is received from the Bus Controller its type is checked, and then stored in the appropriate
SensorData object. Later, when the time comes to transmit the data, these objects are sent to the
Receiver via a link. The partial code for SensorData is given below:
class SensorData { //Utility class to store sensor data
public:
SensorData(string type) //Constructor
: sensor_type(type) {}
string getSensorType() {return sensor_type;}
std::vector<double> getSensorData() {return sensor_data;}
void addData(double newData) {....}
private:
....
std::vector<double> sensor_data;
}; //end class SensorData
LinkAccessController. As indicated above, there are two communications links that can be
accessed simultaneously. The actual number is defined by a global constant:
int const NUM_OF_LINKS = 2;
10
A single instance of the LinkAccessController class, called lac, controls access to these links,
ensuring that they can be used simultaneously. However, if all the links are in use and additional threads
attempt to gain access to one, these latter threads should be suspended until one or more links become
available. Threads should print out their identity, together with their actions, when accessing links: i.e.
when they successfully acquire and release a link, and when (if) they are suspended. The partial code for
LinkAccessController is given below.
class LinkAccessController {
public:
LinkAccessController(Receiver& r) //Constructor
: myReceiver(r), numOfAvailableLinks(NUM_OF_LINKS)
{
for (int i = 0; i < NUM_OF_LINKS; i++) {
commsLinks.push_back(Link(myReceiver, i));
}
}
//Request a comm's link: returns a reference to an available Link.
//If none are available, the calling thread is suspended.
Link& requestLink() {

....
return std::ref(commsLinks[linkNum]);
}
//Release a comms link:
void releaseLink(Link& releasedLink) {
....
}
private:
Receiver& myReceiver; //Receiver reference
int numOfAvailableLinks;
std::vector<Link> commsLinks;
std::mutex LAC_mu; //mutex
....
}; //end class LinkAccessController
Link. Instances of this class represent the communication links in the system. The partial code for Link
class is given below:
class Link {
public:
Link (Receiver& r, int linkNum) //Constructor
: inUse(false), myReceiver(r), linkId(linkNum)
{}
//check if the link is currently in use
bool isInUse() {
return inUse;
}
//set the link status to busy
void setInUse() {
inUse = true;
} continued..
11
//set the link status to idle
void setIdle() {
inUse = false;
}
//write data to the receiver
void writeToDataLink(SensorData sd) {
....
}
//returns the link Id
int getLinkId() {
return linkId;
}
private:
bool inUse;
Receiver& myReceiver; //Receiver reference
int linkId;
}; //end class Link
Receiver. An instance of this class called theReceiver receives the sensor data from the Link
objects via calls to a method called receiveData(), which in turn determines the sensor type and
writes it into the appropriate vector (there is one vector of type double for each sensor type). The partial
code for class Receiver is given below.
class Receiver {
public:
Receiver () { } //constructor
//Receives a SensorData object:
void receiveData(SensorData sd) {
....
}
// print out all data for each sensor:
void printSensorData () {
....
}
private:
//mutex:
....
//vectors to store sensor numeric data received from threads:
std::vector<double> temp_data;
....

}; //end class Receiver
run function.
The run function for each thread should be extended. The following steps should be added to the
sequence given in version A1:
For each sensor type
request access to a link;
the thread should print out its identity and transmit the data for that sensor type to the Receiver;
release the link, printing out its identity once more;
once a link has been released, delay for a random time between 1 and 10 milliseconds.
12
A link can be conveniently obtained using a pointer as follows:
Link* link = &lac.requestLink();
and used to send data to the Receiver with:
link-> writeToDataLink(...); //note pointer syntax
A link can be released with:
lac.releaseLink(*link);
main function.
The main function creates the following objects/data structures:
theBC, sensors and the_threads
-as developed in part A1 of this assignment.
lac
-an object of class LinkAccessController.
theReceiver
-an object of class Receiver.
int main() {
//declare and initialise a vector of pointers to Sensors:
std::vector<Sensor*> sensors;
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 the Receiver:
....
// Instantiate the LinkAccessController:
....
//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:
....
// Print out all the data in the Receiver:
....
cout << "All threads terminated" << endl;
return 0;
}
Notes on the Program
As per A1, 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%. You are also expected to use the global
constants defined in the program in a consistent way throughout.
13
4. Laboratory Arrangements
Your solution to the assignment (A1 or A2) will be demonstrated in Barnes Wallis on your final allocated
lab for Concurrent Systems (please check your timetable for the actual date). Demonstration of your
program at other times is not permitted. You will be expected to upload a copy of the program to
Blackboard for marking during the demonstration.
NOTE CAREFULLY: a learning objective of this assignment is to be able to
explain how a concurrent program works. During the demonstration you will
be asked simple questions about your program, for example the effect on the
program of changing one or more of the constants or variables. Marks will be
deducted if you cannot answer the questions that you are asked.
5. Deliverables
An electronic copy of your program should be uploaded at the end of the final session. Programs without
comments or with few comments will automatically receive low marks.
6. Plagiarism
This is an individual assignment. Any plagiarism will be referred to the Director of Studies and penalties
will be applied in accordance with University policy.

http://www.6daixie.com/contents/13/2118.html

代写EEEN30052作业、代写C/C++语言作业、C/C++程序作业代做、代做Concurrent Systems作业
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 1
2
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 1
3
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 <iostream>
#include <thread>
....
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<Sensor*>& 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<Sensor*>& 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<Sensor*> 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<std::mutex> 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 <std::thread::id, int>::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 <map>
For more info about maps, with more examples, see for example:
http://thispointer.com/stdmap-tutorial-part-1-usage-detail-with-examples/
2.2 Creating a delay by generating random values.
One way to generate say, random integers, is to use a pseudo-random number generator (prng), and 'seed'
it with a value. The code below shows one way of doing this (adapted from
http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution)
std::mt19937 gen(time(0)); //A prng seeded by time(0).
//time(0) gives the number of seconds elapsed since the start
//of the world according to Unix (00:00:00 on 1st January 1970).
std::uniform_int_distribution<> dis(1, 1000); //set up the distribution
//from which integers will be drawn.
int n = dis(gen); //generate a pseudo-random integer between 1-1000.
-the pseudo-random number is stored in n.
You will need the following preprocessor directives in order to use the above example code:
#include <random>
#include <chrono>
Note that only a single generator is necessary, which only needs to be initialised once. Furthermore, a
solution that does not calculate the delay period correctly will lose up to 5% of the total marks.
2.3 Vectors or Arrays?
A vector and an array are similar in that they both contain a list of data, however the basic difference is
that the size of an array is fixed, whereas a vector 'grows' automatically as more items are added to it.
Furthermore, the availability of methods associated with vectors make them very flexible.
In general in C++11 it is straightforward to create arrays of objects of classes from the standard library,
such as std::thread, for example. However, arrays of objects of user-defined classes, such as
Sensor, or Link, are more awkward to use. For such user-defined classes you may find
std::vector much easier to handle, for example:
std::vector<Sensor*> sensors; //vector of pointers to Sensor objects
or std::vector<Link> commsLinks; //vector of Link objects
A example tutorial is:
http://www.codeguru.com/cpp/cpp/cpp_mfc/stl/article.php/c4027/C-Tutorial-A-Beginners-Guide-tostdvector-Part-1.htm
8
2.4 Inheritance
In C++ one class (the 'base' class) can define methods and variables/constants (i.e. class 'members') which
can be inherited by another ('derived' class). In principle this means that such methods/variables/constants
need to be defined only once (thereby promoting re-useability) yet can be used by instances of both base
and derived classes. In the following example, B declares its own members, in addition to those inherited
from A:
#include <iostream>
using namespace std;
class A{ //base class
public:
A (int i) //constructor
: x(i) { } //initialise private instance variable x
void set_x (int a) { x = a; } //set value of x
int get_x ( ) { return x; }
private:
int x; //instance variable
}; //end class A
class B : public A { //derived class
public:
B (int i, int j) //constructor must call A's constructor
: A(i) {p = j;} //initialise private instance variable p
void set_p(int a) { p = a; } //set value of p
int get_p( ) { return p; }
private:
int p; //instance variable
}; //end class B
//The following illustrates use:
int main() {
A a(3); //create instance of A, initialise member variable
//x to 3
a.set_x(4); //change x to new value
cout << a.get_x() << endl; //and print it out
B b(5,4); //create instance of B and initialise member
//variables x to 5 and p to 4
b.set_x(6);//use b uses inherited method to set new value of x
cout << b.get_x() << endl; // use b to print out x
cout << b.get_p() << endl; //and print out p
}
prints out
4
6
4
If a class contains one or more methods that are declared 'virtual' then the class is said to be 'abstract' and
can't be instantiated; however any derived classes from which instances are required are then obliged to
define the method. This is useful if, for some reason it didn't make sense to define the method in the base
class, yet you still wanted derived classes to have it, but define it in different ways. Using the above as an
example, if in class A we had declared set_x() as a virtual method, i.e.
virtual void set_x (int a) = 0;//syntax specifies method as virtual
then any attempt to instantiate A now fails:
9
A a(3); //FAILS –can't make an instance of abstract class A
Because B doesn't implement set_x() the following also fails to compile:
B b(5,4); //FAILS –can't make an instance of B
In order for this statement to compile, B must implement set_x(), for example by writing
void set_x (int a) { x = a; } //set value of x
Inheritance is a feature of object-oriented software design that permits the use of powerful techniques,
such as polymorphism. The following link provides more information:
http://www.cplusplus.com/doc/tutorial/inheritance/
3. A2 – worth 100% of total coursework marks
In A1 we were concerned with creating threads and being able to lock access to the Bus Controller. In this
version we extend the solution to include access to the communications links. In particular, the threads
should request a Link from a LinkAccessController object. When one is available, the sample
values that the thread has acquired can be transmitted. The Link object is used to copy this data to the
Receiver object. To facilitate this, each thread stores sensor data in objects of a class called
SensorData, which stores the samples internally in a vector.
The main program should create and initiate all the threads, and should wait until they have all have
terminated before calling the Receiver’s method that prints out all the sensor data.
Part A2 builds on A1 and requires the following additional classes:
SensorData. As indicated above, data sampled by each thread is stored in a vector managed by
instances of this class. Once started, each thread would create three instances, one for each sensor type.
When a sample is received from the Bus Controller its type is checked, and then stored in the appropriate
SensorData object. Later, when the time comes to transmit the data, these objects are sent to the
Receiver via a link. The partial code for SensorData is given below:
class SensorData { //Utility class to store sensor data
public:
SensorData(string type) //Constructor
: sensor_type(type) {}
string getSensorType() {return sensor_type;}
std::vector<double> getSensorData() {return sensor_data;}
void addData(double newData) {....}
private:
....
std::vector<double> sensor_data;
}; //end class SensorData
LinkAccessController. As indicated above, there are two communications links that can be
accessed simultaneously. The actual number is defined by a global constant:
int const NUM_OF_LINKS = 2;
10
A single instance of the LinkAccessController class, called lac, controls access to these links,
ensuring that they can be used simultaneously. However, if all the links are in use and additional threads
attempt to gain access to one, these latter threads should be suspended until one or more links become
available. Threads should print out their identity, together with their actions, when accessing links: i.e.
when they successfully acquire and release a link, and when (if) they are suspended. The partial code for
LinkAccessController is given below.
class LinkAccessController {
public:
LinkAccessController(Receiver& r) //Constructor
: myReceiver(r), numOfAvailableLinks(NUM_OF_LINKS)
{
for (int i = 0; i < NUM_OF_LINKS; i++) {
commsLinks.push_back(Link(myReceiver, i));
}
}
//Request a comm's link: returns a reference to an available Link.
//If none are available, the calling thread is suspended.
Link& requestLink() {

....
return std::ref(commsLinks[linkNum]);
}
//Release a comms link:
void releaseLink(Link& releasedLink) {
....
}
private:
Receiver& myReceiver; //Receiver reference
int numOfAvailableLinks;
std::vector<Link> commsLinks;
std::mutex LAC_mu; //mutex
....
}; //end class LinkAccessController
Link. Instances of this class represent the communication links in the system. The partial code for Link
class is given below:
class Link {
public:
Link (Receiver& r, int linkNum) //Constructor
: inUse(false), myReceiver(r), linkId(linkNum)
{}
//check if the link is currently in use
bool isInUse() {
return inUse;
}
//set the link status to busy
void setInUse() {
inUse = true;
} continued..
11
//set the link status to idle
void setIdle() {
inUse = false;
}
//write data to the receiver
void writeToDataLink(SensorData sd) {
....
}
//returns the link Id
int getLinkId() {
return linkId;
}
private:
bool inUse;
Receiver& myReceiver; //Receiver reference
int linkId;
}; //end class Link
Receiver. An instance of this class called theReceiver receives the sensor data from the Link
objects via calls to a method called receiveData(), which in turn determines the sensor type and
writes it into the appropriate vector (there is one vector of type double for each sensor type). The partial
code for class Receiver is given below.
class Receiver {
public:
Receiver () { } //constructor
//Receives a SensorData object:
void receiveData(SensorData sd) {
....
}
// print out all data for each sensor:
void printSensorData () {
....
}
private:
//mutex:
....
//vectors to store sensor numeric data received from threads:
std::vector<double> temp_data;
....

}; //end class Receiver
run function.
The run function for each thread should be extended. The following steps should be added to the
sequence given in version A1:
For each sensor type
request access to a link;
the thread should print out its identity and transmit the data for that sensor type to the Receiver;
release the link, printing out its identity once more;
once a link has been released, delay for a random time between 1 and 10 milliseconds.
12
A link can be conveniently obtained using a pointer as follows:
Link* link = &lac.requestLink();
and used to send data to the Receiver with:
link-> writeToDataLink(...); //note pointer syntax
A link can be released with:
lac.releaseLink(*link);
main function.
The main function creates the following objects/data structures:
theBC, sensors and the_threads
-as developed in part A1 of this assignment.
lac
-an object of class LinkAccessController.
theReceiver
-an object of class Receiver.
int main() {
//declare and initialise a vector of pointers to Sensors:
std::vector<Sensor*> sensors;
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 the Receiver:
....
// Instantiate the LinkAccessController:
....
//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:
....
// Print out all the data in the Receiver:
....
cout << "All threads terminated" << endl;
return 0;
}
Notes on the Program
As per A1, 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%. You are also expected to use the global
constants defined in the program in a consistent way throughout.
13
4. Laboratory Arrangements
Your solution to the assignment (A1 or A2) will be demonstrated in Barnes Wallis on your final allocated
lab for Concurrent Systems (please check your timetable for the actual date). Demonstration of your
program at other times is not permitted. You will be expected to upload a copy of the program to
Blackboard for marking during the demonstration.
NOTE CAREFULLY: a learning objective of this assignment is to be able to
explain how a concurrent program works. During the demonstration you will
be asked simple questions about your program, for example the effect on the
program of changing one or more of the constants or variables. Marks will be
deducted if you cannot answer the questions that you are asked.
5. Deliverables
An electronic copy of your program should be uploaded at the end of the final session. Programs without
comments or with few comments will automatically receive low marks.
6. Plagiarism
This is an individual assignment. Any plagiarism will be referred to the Director of Studies and penalties
will be applied in accordance with University policy.

http://www.6daixie.com/contents/13/2118.html

因为专业,所以值得信赖。如有需要,请加QQ99515681 或邮箱:[email protected] 

微信:codinghelp

猜你喜欢

转载自www.cnblogs.com/ghostpython/p/9943343.html