Threads for Parallel Programming
CS 301 Lecture, Dr. Lawlor
The standard way to write parallel code to run on multicore hardware is
using "threads". A thread is basically just a function that runs
at the same time as your other functions. To do this, a thread
has its own stack and its own set of registers, but shares memory with
the other threads in the same program. You can make "user-level"
threads yourself, like we did on Wednesday, but to run on multiple
cores you usually just ask the operating system to make new threads.
On Windows, there's a function named CreateThread to do this (here's an example).
On UNIX machines like Mac OS X or NetRun, the interface is called pthreads.
Here's how you make a pthread to run "fnB" at the same time as "fnA":
#include <pthread.h>
pthread_t A, B; /* threads to run */
void fnA(void) { /* runs from main thread */
for (int i=0;i<100;i++) {
std::cout<<"A:"<<i<<"\n"; std::cout.flush();
}
}
void *fnB(void *somePointer) { /* runs from new pthread */
for (int i=0;i<100;i++) {
std::cout<<" B:"<<i<<"\n"; std::cout.flush();
}
return 0;
}
int foo(void) {
pthread_create(&B,0, fnB,NULL); /* make a new thread to run fnB */
fnA();
void *someReturn;
pthread_join(B,&someReturn); /* wait until fnB is finished running */
std::cout<<"Back to foo\n";
return 0;
}
(Try this in NetRun now!)
There are a few things to be careful about with threads:
- Creating a thread (pthread_create) is relatively expensive, something like 20,000 nanoseconds.
To actually speed up the program, the amount of work in the thread has
to be more than this, usually millions of operations, or it'll just
slow things down.
- Threads can run AT THE SAME TIME. This means you have to be
very careful about accessing the one piece of data from two threads,
because it's easy to try to do two different things at the same time,
and get the wrong answer (or hang, or crash). In particular,
global variables are shared between all threads, so any modifications
to global variables are likely to give extremely weird inconsistent
answers.