Principles of Computer Systems
Autumn 2019
Stanford University
Computer Science Department
Lecturers: Chris Gregg and
Philip Levis
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
int pthread_join(pthread_t thread, void **retval);
for (size_t i = 0; i < kNumFriends; i++)
pthread_create(&friends[i], NULL, meetup, &i);
for (size_t j = 0; j < kNumFriends; j++)
pthread_join(friends[j], NULL);
bug on line 2!
for (size_t i = 0; i < kNumFriends; i++)
pthread_create(&friends[i], NULL, meetup, &i);
for (size_t j = 0; j < kNumFriends; j++)
pthread_join(friends[j], NULL);
_start
meetup
main
argc
argv
i
args
args
args
args
args
args
created thread stacks
main stack
uint64_t increment_counter(void) {
pthread_mutex_lock(&counter_lock);
counter++;
uint64_t val = counter;
pthread_mutex_unlock(&counter_lock);
return val;
}
uint64_t increment_counter(void) {
lock_guard<mutex> lg(&counter_lock);
counter++;
uint64_t val = counter;
return val;
}
pthread approach
If you forget to unlock, deadlock
C++ approach
Can't forget to unlock!
static void recharge() {
cout << oslock << "I recharge by spending time alone." << endl << osunlock;
}
static const size_t kNumIntroverts = 6;
int main(int argc, char *argv[]) {
cout << "Let's hear from " << kNumIntroverts << " introverts." << endl
thread introverts[kNumIntroverts]; // declare array of empty thread handles
for (thread& introvert: introverts)
introvert = thread(recharge); // move anonymous threads into empty handles
for (thread& introvert: introverts)
introvert.join();
cout << "Everyone's recharged!" << endl;
return 0;
}
static void *recharge(void *args) {
printf("I recharge by spending time alone.\n");
return NULL;
}
static const size_t kNumIntroverts = 6;
int main(int argc, char *argv[]) {
printf("Let's hear from %zu introverts.\n", kNumIntroverts);
pthread_t introverts[kNumIntroverts];
for (size_t i = 0; i < kNumIntroverts; i++)
pthread_create(&introverts[i], NULL, recharge, NULL);
for (size_t i = 0; i < kNumIntroverts; i++)
pthread_join(introverts[i], NULL);
printf("Everyone's recharged!\n");
return 0;
}
C/pthreads
C++
thread introverts[kNumIntroverts]; // declare array of empty thread handles
for (thread& introvert: introverts)
introvert = thread(recharge); // move anonymous threads into empty handles
for (thread& introvert: introverts)
introvert.join();
static void greet(size_t id) {
for (size_t i = 0; i < id; i++) {
cout << oslock << "Greeter #" << id << " says 'Hello!'" << endl << osunlock;
struct timespec ts = {
0, random() % 1000000000
};
nanosleep(&ts, NULL);
}
cout << oslock << "Greeter #" << id << " has issued all of his hellos, "
<< "so he goes home!" << endl << osunlock;
}
static const size_t kNumGreeters = 6;
int main(int argc, char *argv[]) {
cout << "Welcome to Greetland!" << endl;
thread greeters[kNumGreeters];
for (size_t i = 0; i < kNumGreeters; i++) greeters[i] = thread(greet, i + 1);
for (thread& greeter: greeters) greeter.join();
cout << "Everyone's all greeted out!" << endl;
return 0;
}
int main(int argc, const char *argv[]) {
thread processors[10];
size_t remainingImages = 250;
for (size_t i = 0; i < 10; i++)
processors[i] = thread(process, 101 + i, ref(remainingImages));
for (thread& proc: processors) proc.join();
cout << "Images done!" << endl;
return 0;
}
static void process(size_t id, size_t& remainingImages) {
while (remainingImages > 0) {
processImage(remainingImages);
remainingImages--;
cout << oslock << "Thread#" << id << " processed an image (" << remainingImages
<< " remain)." << endl << osunlock;
}
cout << oslock << "Thread#" << id << " sees no remaining images and exits."
<< endl << osunlock;
}
myth60 ~../cs110/cthreads -> ./imagethreads
Thread# 109 processed an image, 249 remain
Thread# 102 processed an image, 248 remain
Thread# 101 processed an image, 247 remain
Thread# 104 processed an image, 246 remain
Thread# 108 processed an image, 245 remain
Thread# 106 processed an image, 244 remain
// 241 lines removed for brevity
Thread# 110 processed an image, 3 remain
Thread# 103 processed an image, 2 remain
Thread# 105 processed an image, 1 remain
Thread# 108 processed an image, 0 remain
Thread# 105 processed an image, 18446744073709551615 remain
Thread# 109 processed an image, 18446744073709551614 remain
0x0000000000401a9b <+36>: mov -0x20(%rbp),%rax
0x0000000000401a9f <+40>: mov (%rax),%eax
0x0000000000401aa1 <+42>: lea -0x1(%rax),%edx
0x0000000000401aa4 <+45>: mov -0x20(%rbp),%rax
0x0000000000401aa8 <+49>: mov %edx,(%rax)
class mutex {
public:
mutex(); // constructs the mutex to be in an unlocked state
void lock(); // acquires the lock on the mutex, blocking until it's unlocked
void unlock(); // releases the lock and wakes up another threads trying to lock it
};
static void process(size_t id, size_t& remainingImages, mutex& counterLock) {
while (true) {
counterLock.lock();
if (remainingImages == 0) {
counterLock.unlock();
break;
}
processImage(remainingImages);
remainingImages--;
cout << oslock << "Thread#" << id << " processed an image (" << remainingImages
<< " remain)." << endl << osunlock;
counterLock.unlock();
}
cout << oslock << "Thread#" << id << " sees no remaining images and exits."
<< endl << osunlock;
}
int main(int argc, const char *argv[]) {
size_t remainingImages = 250;
mutex counterLock;
thread processors[10];
for (size_t i = 0; i < 10; i++)
agents[i] = thread(process, 101 + i, ref(remainingImages), ref(counterLock));
for (thread& agent: agents) agent.join();
cout << "Done processing images!" << endl;
return 0;
}
static void process(size_t id, size_t& remainingImages, mutex& counterLock) {
while (true) {
size_t myImage;
counterLock.lock(); // Start of critical section
if (remainingImages == 0) {
counterLock.unlock(); // Rather keep it here, easier to check
break;
} else {
myImage = remainingImages;
remainingImages--;
counterLock.unlock(); // end of critical section
processImage(myImage);
cout << oslock << "Thread#" << id << " processed an image (" << remainingImages
<< " remain)." << endl << osunlock;
}
}
cout << oslock << "Thread#" << id << " sees no remaining images and exits."
<< endl << osunlock;
}
fork
/ execvp
child processes and manage them through the use of signal handlers. It also tests your ability to use pipes.assign3/samples/stsh_soln
)ls | grep stsh | cut -d- -f2
stsh.cc