(NOTE: the inter-connection network is irrelevant to the programming and has been omitted).
|
The reason for this is safety and reliability:
|
Example:
Definition:
In other words: a thread makes progress in executing a program |
Conclusion:
|
These are called SINGLE threaded programs
We will soon see MULTI threaded programs
|
|
There is at least one thread within every process (otherwise, the process will not run :-))
A single threaded process has exactly one thread...
|
(This is assuming we have at least 5 CPUs !!!)
#include <pthread.h> pthread_t tid[100]; // Each thread executes the following function: void *worker(void *arg) { int i; i = pthread_self(); cout << "Hello, I am thread # " << i << endl; return(NULL); /* Thread exits (dies) */ } /* ======================= MAIN ======================= */ int main(int argc, char *argv[]) { int i, num_threads; num_threads = atoi(argv[1]); /* ------ Create threads ------ */ for (i = 0; i < num_threads; i = i + 1) { if ( pthread_create(&tid[i], NULL, worker, NULL) ) { cout << "Cannot create thread" << endl; exit(1); } } // Wait for all threads to terminate for (i = 0; i < num_threads; i = i + 1) pthread_join(tid[i], NULL); exit(0); } |
|
|
Compile with: CC -mt thread01.C
Run the program several times and you will notice that the threads prints things out in different order...
We do not have control on when each thread is run....
High level programming language: i = i + 1; (Assuming variable i is stored at address 2000) Assembler code: 1. read value from memory address 2000 into the CPU 2. add 1 to this value 3. write value from CPU to memory address 2000 |
This detail will be important when we have multiple threads executing in a program.
#include <pthread.h> int N; pthread_t tid[100]; // Each thread executes the following function: void *worker(void *arg) { int i, k, s; for (i = 0; i < 10000; i = i + 1) { N = N + 1; } cout << "Added 10000 to N" << endl; return(NULL); /* Thread exits (dies) */ } /* ======================= MAIN ======================= */ int main(int argc, char *argv[]) { int i, num_threads; num_threads = atoi(argv[1]); /* ------ Create threads ------ */ for (i = 0; i < num_threads; i = i + 1) { if ( pthread_create(&tid[i], NULL, worker, NULL) ) { cout << "Cannot create thread" << endl; exit(1); } } N = 0; // Wait for all threads to terminate for (i = 0; i < num_threads; i = i + 1) pthread_join(tid[i], NULL); cout << "N = " << N << endl << endl; exit(0); } |
|
|
Threads that are ready to run will be executed in an order determined by the thread scheduling system
(The programmer does not (nor should ) have control over the therad scheduling --- you could do some thead scheduling, but it is not worth the effort)
The global variables are shared by all threads and can thus be used by threads to "deposit" values for one another --- through the writing and reading of global variables
|
Thread 1 on Thread 2 on Memory CPU 1 CPU 2 ============== =================== ================= N = 1234 Read N --> 1234 Add 1 --> 1235 Read N --> 1234 N = 1235 Write N Add 1 --> 1235 N = 1235 Write N |
|
|
|
Example: non-shared local variable
void *worker(void *arg) { int N; int i; // Local i (non-shared) N = 0; for (i = 0; i < 10000; i = i + 1) { N = N + 1; } cout << "Added 10000 to N, N = " << N << endl << endl; return(NULL); /* Thread exits (dies) */ } /* ======================= MAIN ======================= */ int main(int argc, char *argv[]) { int i, num_threads; num_threads = ... /* ------ Create threads ------ */ for (i = 0; i < num_threads; i = i + 1) { if ( pthread_create(&tid[i], NULL, worker, NULL) ) { cout << "Cannot create thread" << endl; exit(1); } } for (i = 0; i < num_threads; i = i + 1) pthread_join(tid[i], NULL); } |
Compile and run:
CC -mt thread03.C a.out |
Because the variable i is non-shared , this variable kept an accurate count within the loop
int i; // Global i (shared !!!) void *worker(void *arg) { int N; N = 0; for (i = 0; i < 10000; i = i + 1) { N = N + 1; } cout << "Added 10000 to N, N = " << N << endl << endl; return(NULL); /* Thread exits (dies) */ } /* ======================= MAIN ======================= */ int main(int argc, char *argv[]) { int i, num_threads; num_threads = ... /* ------ Create threads ------ */ for (i = 0; i < num_threads; i = i + 1) { if ( pthread_create(&tid[i], NULL, worker, NULL) ) { cout << "Cannot create thread" << endl; exit(1); } } for (i = 0; i < num_threads; i = i + 1) pthread_join(tid[i], NULL); } |
Compile and run:
CC -mt thread03b.C a.out |
It did NOT keep an accurate count within the loop.