第一个问题就是实现和测试 fork/join 问题的解决方案,如本文所述。 即使在文本中描述了此解决方案, 重新自己实现一遍也是值得的。 even Bach would rewrite Vivaldi, allowing one soon-to-be master to learn from an existing one。 有关详细信息,请参见 fork-join.c。 将添加 sleep(1) 到 child 函数内以确保其正常工作。
解答:题目要求我们补全fork-join.c
然后按顺序打印
parent:begin
child
parent:end
完整的代码如下:
#include#include #include #include "common_threads.h" #include sem_t s; void *child(void *arg) { sleep(1); printf("childn"); sem_post(&s); return NULL; } int main(int argc, char *argv[]) { pthread_t p; printf("parent: beginn"); sem_init(&s, 0, 0); Pthread_create(&p, NULL, child, NULL); sem_wait(&s); printf("parent: endn"); return 0; }
输入命令gcc -o fork-join fork-join.c -Wall -pthread查看运行结果
现在,我们通过研究集合点问题 rendezvous problem 来对此进行概括。 问题如下:您有两个线程,每个线程将要在代码中进入集合点。 任何一方都不应在另一方进入之前退出代码的这一部分。 该任务使用两个信号量,有关详细信息,请参见 rendezvous.c。
解答:题目要求先打印两个before,再打印两个after
完整的代码如下:
#include#include #include "common_threads.h" #include // If done correctly, each child should print their "before" message // before either prints their "after" message. Test by adding sleep(1) // calls in various locations. sem_t s1, s2; void *child_1(void *arg) { printf("child 1: beforen"); sleep(1); sem_post(&s2); sem_wait(&s1); printf("child 1: aftern"); return NULL; } void *child_2(void *arg) { printf("child 2: beforen"); sleep(1); sem_post(&s1); sem_wait(&s2); printf("child 2: aftern"); return NULL; } int main(int argc, char *argv[]) { pthread_t p1, p2; printf("parent: beginn"); sem_init(&s1, 0, 0); sem_init(&s2, 0, 0); Pthread_create(&p1, NULL, child_1, NULL); Pthread_create(&p2, NULL, child_2, NULL); Pthread_join(p1, NULL); Pthread_join(p2, NULL); printf("parent: endn"); return 0; }
输入命令gcc -o a rendezvous.c -Wall -pthread 查看运行结果
现在按照文本中所述,解决读者写者问题。 首先,不用考虑进程饥饿。 有关详细信息,请参见 reader-writer.c 中的代码。 将 sleep()调用添加到您的代码中,以证明它可以按预期工作。 你能证明饥饿问题的存在吗?
解答题目要求我们补充reader-writer.c 中的函数代码
完整代码如下:
#include#include #include #include "common_threads.h" #include // // Your code goes in the structure and functions below // typedef struct __rwlock_t { sem_t lock; sem_t write_lock; int reader_number; } rwlock_t; void rwlock_init(rwlock_t *rw) { sem_init(&rw->lock, 0, 1); sem_init(&rw->write_lock, 0, 1); rw->reader_number = 0; } void rwlock_acquire_readlock(rwlock_t *rw) { sleep(1); sem_wait(&rw->lock); rw->reader_number++; if (rw->reader_number == 1) { sem_wait(&rw->write_lock); } sem_post(&rw->lock); } void rwlock_release_readlock(rwlock_t *rw) { sem_wait(&rw->lock); rw->reader_number--; if (rw->reader_number == 0) { sem_post(&rw->write_lock); } sem_post(&rw->lock); } void rwlock_acquire_writelock(rwlock_t *rw) { sleep(1); sem_wait(&rw->write_lock); } void rwlock_release_writelock(rwlock_t *rw) { sem_post(&rw->write_lock); } // // Don't change the code below (just use it!) // int loops; int value = 0; rwlock_t lock; void *reader(void *arg) { int i; for (i = 0; i < loops; i++) { rwlock_acquire_readlock(&lock); printf("read %dn", value); rwlock_release_readlock(&lock); } return NULL; } void *writer(void *arg) { int i; for (i = 0; i < loops; i++) { rwlock_acquire_writelock(&lock); value++; printf("write %dn", value); rwlock_release_writelock(&lock); } return NULL; } int main(int argc, char *argv[]) { assert(argc == 4); int num_readers = atoi(argv[1]); int num_writers = atoi(argv[2]); loops = atoi(argv[3]); pthread_t pr[num_readers], pw[num_writers]; rwlock_init(&lock); printf("beginn"); int i; for (i = 0; i < num_readers; i++) Pthread_create(&pr[i], NULL, reader, NULL); for (i = 0; i < num_writers; i++) Pthread_create(&pw[i], NULL, writer, NULL); for (i = 0; i < num_readers; i++) Pthread_join(pr[i], NULL); for (i = 0; i < num_writers; i++) Pthread_join(pw[i], NULL); printf("end: value %dn", value) return 0; }
程序三个参数分别为读者数,写者数,每个读者、写者进行的读写操作数
当读者数量远大于写者时,写者可能饿死,读者不需要锁就能进入临界区,只要有一个读者获得锁,
其他读者线程就能运行,读者数量可能一直大于0,而写者始终无法获取锁
运行命令
gcc -o reader-writer reader-writer.c -Wall -pthread ./reader-writer 5 5 10
查看结果
31.5 问题描述让我们再次看一下读者写者问题,但这一次需要考虑进程饥饿。 您如何确保所有读者和写者运行? 有关详细信息,请参见 reader-writer-nostarve.c。
解答新增 write_waiting 锁,写者读者都需要竞争这个锁。新增读者要持有这把锁,写者进行写也要持有这把锁,一旦写者抢到了这把锁,就不能增加新读者,直到写者完成写(写者要等当前所有读者离开才能写)
完整代码如下:
#include#include #include #include "common_threads.h" #include // // Your code goes in the structure and functions below // typedef struct __rwlock_t { sem_t lock; sem_t write_lock; sem_t write_waiting; int reader_number; } rwlock_t; void rwlock_init(rwlock_t *rw) { sem_init(&rw->lock, 0, 1); sem_init(&rw->write_lock, 0, 1); sem_init(&rw->write_waiting, 0, 1); rw->reader_number = 0; } void rwlock_acquire_readlock(rwlock_t *rw) { sleep(1); sem_wait(&rw->write_waiting); sem_wait(&rw->lock); rw->reader_number++; if (rw->reader_number == 1) { sem_wait(&rw->write_lock); } sem_post(&rw->lock); sem_post(&rw->write_waiting); } void rwlock_release_readlock(rwlock_t *rw) { sem_wait(&rw->lock); rw->reader_number--; if (rw->reader_number == 0) { sem_post(&rw->write_lock); } sem_post(&rw->lock); } void rwlock_acquire_writelock(rwlock_t *rw) { sleep(1); sem_wait(&rw->write_waiting); sem_wait(&rw->write_lock); sem_post(&rw->write_waiting); } void rwlock_release_writelock(rwlock_t *rw) { sem_post(&rw->write_lock); } // // Don't change the code below (just use it!) // int loops; int value = 0; rwlock_t lock; void *reader(void *arg) { int i; for (i = 0; i < loops; i++) { rwlock_acquire_readlock(&lock); printf("read %dn", value); rwlock_release_readlock(&lock); } return NULL; } void *writer(void *arg) { int i; for (i = 0; i < loops; i++) { rwlock_acquire_writelock(&lock); value++; printf("write %dn", value); rwlock_release_writelock(&lock); } return NULL; } int main(int argc, char *argv[]) { assert(argc == 4); int num_readers = atoi(argv[1]); int num_writers = atoi(argv[2]); loops = atoi(argv[3]); pthread_t pr[num_readers], pw[num_writers]; rwlock_init(&lock); printf("beginn"); int i; for (i = 0; i < num_readers; i++) Pthread_create(&pr[i], NULL, reader, NULL); for (i = 0; i < num_writers; i++) Pthread_create(&pw[i], NULL, writer, NULL); for (i = 0; i < num_readers; i++) Pthread_join(pr[i], NULL); for (i = 0; i < num_writers; i++) Pthread_join(pw[i], NULL); printf("end: value %dn", value); return 0; }
输入命令
gcc -o a reader-writer-nostarve.c -Wall -pthread ./a 5 5 10
查看运行结果:
31.6 问题描述使用信号量构建一个没有饥饿的互斥量,其中任何试图获取该互斥量的线程都将最终获得它。 有关更多信息,请参见 mutex-nostarve.c 中的代码。
解答这题的主要原理为释放临界区锁后,调用barrier.c内实现的barrier函数,等待所有线程执行完再继续进行
完整代码如下:
#include#include #include #include #include "common_threads.h" #include "semaphore.h" // // Here, you have to write (almost) ALL the code. Oh no! // How can you show that a thread does not starve // when attempting to acquire this mutex you build? // typedef struct __ns_mutex_t { sem_t mutex; sem_t barrier; sem_t lock; int num_threads; } ns_mutex_t; ns_mutex_t m; void ns_mutex_init(ns_mutex_t *m, int num_threads) { sem_init(&m->barrier, 0, -num_threads + 1); sem_init(&m->mutex, 0, 1);//互斥锁 } void ns_mutex_acquire(ns_mutex_t *m) { sem_wait(&m->lock); } void ns_mutex_release(ns_mutex_t *m) { sem_post(&m->lock); sem_post(&m->barrier); sem_wait(&m->barrier); sem_wait(&m->mutex); for (int i = 0; i < m->num_threads; i++) { sem_post(&m->barrier); //确认所有线程都执行过临界区后再继续执行 } sem_post(&m->mutex); } void *worker(void *arg) { return NULL; } int main(int argc, char *argv[]) { printf("parent: beginn"); printf("parent: endn"); return 0; }
输入命令运行结果如下:



