I3MUP1+Exercise+3

toc =Exercise 3.1=

=Exercise 3.2=

Exercise 3.2.1
//Write a program that creates two threads. When created, the threads must be passed an ID which they will// //print to stdout every second along with the number of times the thread has printed to stdout.//

First we wrote our "Hello" function. Hello is a function that returns a void pointer and takes a void pointer as its argument. The reason for this is that the only parameter you can pass when creating a thread is a void pointer.

//Below are selected relevant code examples from our source code://

Function call used in main to create a thread. code format="cpp" // creates a thread using a pthread object, our hello function, and a void pointer to the int ID used by the function int r1 = pthread_create(&thread1, NULL, hello, (void *)(&id1)); code The function itself is simple enough as it outputs a message 10 times along with the ID passed as its argument. The argument is typecast to an int pointer which is then dereferenced. code format="cpp" void * hello(void * argv) {   for(int i = 0; i<10 ; i++) {       cout << "Hello #" << i << " from thread " << *(int *)(argv) << endl; sleep(1); }

return NULL; } code

To prevent our main function from returning before the threads finish we use pthread_join. Below you will see the resulting output

Exercise 3.2.2
//Create a program that creates two threads, incrementer and reader. The two threads share an unsigned// //integer variable shared which is initially 0. incrementer increments shared every second while reader// //reads it every second and outputs it to stdout.//

We write two functions, 'incrementer' and 'reader'. Their implementation is shown below:

code format="cpp" void * incrementer(void * shared) {   while(1){ (*(int*)(shared))++; sleep(1); }   return NULL; } code

code format="cpp" void * reader(void * shared) {   while(1) {       sleep(1); cout << *(int*)(shared) << endl; }   return NULL; }

code

Our main function creates two threads, 'increment' and 'read', passing the 'shared' variable to both of them as argument. code format="cpp" int r1 = pthread_create(&increment, NULL, incrementer, &shared); int r2 = pthread_create(&read, NULL, reader, &shared); code

The program is compiled and tested successfully on both host and target. Example output shown below: code MAIN: Creating threads... 2 3 4 5 6 7 8 9 10 code We don't see any errors in the execution of the program, but since they both access the same variable, we risk running into the Shared Data problem, which means the reader thread may try to read from the shared variable before the incrementer thread has incremented it.

Exercise 3.2.3 & 3.2.4
//Create a thread functions: writer that uses Vector::setAndTest to set and test the value of a shared// //Vector object.// //Modify your program from exercise 3 so that the writers loop time is no longer one second but a userdefined number of microseconds.//

In this exercise, we have a writer function run by a user-defined number of threads simultaneously. The writer function is shown below. The arguments passed to the function are contained in a struct, and include a pointer to a shared vector object, an integer containing the thread ID, and a userdefined value defining how long the threads sleep between each modification of the vector object. The sleepTime variable is only used in the last exercise, but the changes to the code to implement it were minimal, so we're describing them together.

code format="cpp" void * writer(void * struct_ptr) {	int myId = (*(writerData *)struct_ptr).id; long int sleepTime = (*(writerData *)struct_ptr).usec; bool tester; for(int i = 0; i<10000; i++) {

tester = ((writerData *)struct_ptr)->myVector->setAndTest(myId); if ( tester == false ) {			cout << "\nThread # " << myId << " failed!"; }

usleep(sleepTime); }	return NULL; } code

Our main function prompts the user for a number of thread (1-100) and how long the threads should sleep for, in microseconds. We use dynamically created arrays to store our thread-handles and struct-pointers, although using STL vectors for this would probably have been a more elegant solution. Our structs and threads are created in a for-loop, where a pointer to the newly created struct is passed as argument to the writer thread. After experimenting with different numbers of threads and sleeptimes, we found that a low number of threads, or a long sleeptime led to fewer errors. By increasing the number of threads, or lowering the sleeptime, we could increase the number of errors drastically. Errors occur when a thread is interrupted by the OS before it finishes initializing and testing all values in the vector, which can lead to inconsistencies when another thread is testing the vector data. It was generally 'easier' to provoke failure on the target than on the host, which may be related to the host being more powerful.

Example output from program execution on the host is shown below.