30 条件变量

  1. void *child(void *arg) {
  2. printf("child\n");
  3. // XXX how to indicate we are done?
  4. return NULL;
  5. }
  6. int main(int argc, char *argv[]) {
  7. printf("parent: begin\n");
  8. pthread_t c;
  9. Pthread_create(&c, NULL, child, NULL); // create child
  10. // XXX how to wait for child?
  11. printf("parent: end\n");
  12. return 0;
  13. }

Figure 30.1: A Parent Waiting For Its Child

  1. volatile int done = 0;
  2. void *child(void *arg) {
  3. printf("child\n");
  4. done = 1;
  5. return NULL;
  6. }
  7. int main(int argc, char *argv[]) {
  8. printf("parent: begin\n");
  9. pthread_t c;
  10. Pthread_create(&c, NULL, child, NULL); // create child
  11. while (done == 0)
  12. ; // spin
  13. printf("parent: end\n");
  14. return 0;
  15. }

Figure 30.2: Parent Waiting For Child: Spin-based Approach

  1. int done = 0;
  2. pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
  3. pthread_cond_t c = PTHREAD_COND_INITIALIZER;
  4. void thr_exit() {
  5. Pthread_mutex_lock(&m);
  6. done = 1;
  7. Pthread_cond_signal(&c);
  8. Pthread_mutex_unlock(&m);
  9. }
  10. void *child(void *arg) {
  11. printf("child\n");
  12. thr_exit();
  13. return NULL;
  14. }
  15. void thr_join() {
  16. Pthread_mutex_lock(&m);
  17. while (done == 0)
  18. Pthread_cond_wait(&c, &m);
  19. Pthread_mutex_unlock(&m);
  20. }
  21. int main(int argc, char *argv[]) {
  22. printf("parent: begin\n");
  23. pthread_t p;
  24. Pthread_create(&p, NULL, child, NULL);
  25. thr_join();
  26. printf("parent: end\n");
  27. return 0;
  28. }

Figure 30.3: Parent Waiting For Child: Use A Condition Variable

30.1 Definition and Routines

30.2 生产者消费者(有界缓存)问题

  1. int buffer;
  2. int count = 0; // initially, empty
  3. void put(int value) {
  4. assert(count == 0);
  5. count = 1;
  6. buffer = value;
  7. }
  8. int get() {
  9. assert(count == 1);
  10. count = 0;
  11. return buffer;
  12. }

Figure 30.4: The Put And Get Routines (Version 1)

  1. void *producer(void *arg) {
  2. int i;
  3. int loops = (int) arg;
  4. for (i = 0; i < loops; i++) {
  5. put(i);
  6. }
  7. }
  8. void *consumer(void *arg) {
  9. int i;
  10. while (1) {
  11. int tmp = get();
  12. printf("%d\n", tmp);
  13. }
  14. }

Figure 30.5: Producer/Consumer Threads (Version 1)

  1. cond_t cond;
  2. mutex_t mutex;
  3. void *producer(void *arg) {
  4. int i;
  5. for (i = 0; i < loops; i++) {
  6. Pthread_mutex_lock(&mutex); // p1
  7. if (count == 1) // p2
  8. Pthread_cond_wait(&cond, &mutex); // p3
  9. put(i); // p4
  10. Pthread_cond_signal(&cond); // p5
  11. Pthread_mutex_unlock(&mutex); // p6
  12. }
  13. }
  14. void *consumer(void *arg) {
  15. int i;
  16. for (i = 0; i < loops; i++) {
  17. Pthread_mutex_lock(&mutex); // c1
  18. if (count == 0) // c2
  19. Pthread_cond_wait(&cond, &mutex); // c3
  20. int tmp = get(); // c4
  21. Pthread_cond_signal(&cond); // c5
  22. Pthread_mutex_unlock(&mutex); // c6
  23. printf("%d\n", tmp);
  24. }
  25. }

Figure 30.6: Producer/Consumer: Single CV And If Statement

  1. cond_t cond;
  2. mutex_t mutex;
  3. void *producer(void *arg) {
  4. int i;
  5. for (i = 0; i < loops; i++) {
  6. Pthread_mutex_lock(&mutex); // p1
  7. while (count == 1) // p2
  8. Pthread_cond_wait(&cond, &mutex); // p3
  9. put(i); // p4
  10. Pthread_cond_signal(&cond); // p5
  11. Pthread_mutex_unlock(&mutex); // p6
  12. }
  13. }
  14. void *consumer(void *arg) {
  15. int i;
  16. for (i = 0; i < loops; i++) {
  17. Pthread_mutex_lock(&mutex); // c1
  18. while (count == 0) // c2
  19. Pthread_cond_wait(&cond, &mutex); // c3
  20. int tmp = get(); // c4
  21. Pthread_cond_signal(&cond); // c5
  22. Pthread_mutex_unlock(&mutex); // c6
  23. printf("%d\n", tmp);
  24. }
  25. }

Figure 30.8: Producer/Consumer: Single CV And While

  1. cond_t empty, fill;
  2. mutex_t mutex;
  3. void *producer(void *arg) {
  4. int i;
  5. for (i = 0; i < loops; i++) {
  6. Pthread_mutex_lock(&mutex);
  7. while (count == 1)
  8. Pthread_cond_wait(&empty, &mutex);
  9. put(i);
  10. Pthread_cond_signal(&fill);
  11. Pthread_mutex_unlock(&mutex);
  12. }
  13. }
  14. void *consumer(void *arg) {
  15. int i;
  16. for (i = 0; i < loops; i++) {
  17. Pthread_mutex_lock(&mutex);
  18. while (count == 0)
  19. Pthread_cond_wait(&fill, &mutex);
  20. int tmp = get();
  21. Pthread_cond_signal(&empty);
  22. Pthread_mutex_unlock(&mutex);
  23. printf("%d\n", tmp);
  24. }
  25. }

Figure 30.10: Producer/Consumer: Two CVs And While

  1. int buffer[MAX];
  2. int fill = 0;
  3. int use = 0;
  4. int count = 0;
  5. void put(int value) {
  6. buffer[fill] = value;
  7. fill = (fill + 1) % MAX;
  8. count++;
  9. }
  10. int get() {
  11. int tmp = buffer[use];
  12. use = (use + 1) % MAX;
  13. count--;
  14. return tmp;
  15. }

Figure 30.11: The Final Put And Get Routines

  1. cond_t empty, fill;
  2. mutex_t mutex;
  3. void *producer(void *arg) {
  4. int i;
  5. for (i = 0; i < loops; i++) {
  6. Pthread_mutex_lock(&mutex); // p1
  7. while (count == MAX) // p2
  8. Pthread_cond_wait(&empty, &mutex); // p3
  9. put(i); // p4
  10. Pthread_cond_signal(&fill); // p5
  11. Pthread_mutex_unlock(&mutex); // p6
  12. }
  13. }
  14. void *consumer(void *arg) {
  15. int i;
  16. for (i = 0; i < loops; i++) {
  17. Pthread_mutex_lock(&mutex); // c1
  18. while (count == 0) // c2
  19. Pthread_cond_wait(&fill, &mutex); // c3
  20. int tmp = get(); // c4
  21. Pthread_cond_signal(&empty); // c5
  22. Pthread_mutex_unlock(&mutex); // c6
  23. printf("%d\n", tmp);
  24. }
  25. }

Figure 30.12: The Final Working Solution

30.3 Covering Conditions

  1. // how many bytes of the heap are free?
  2. int bytesLeft = MAX_HEAP_SIZE;
  3. // need lock and condition too
  4. cond_t c;
  5. mutex_t m;
  6. void *
  7. allocate(int size) {
  8. Pthread_mutex_lock(&m);
  9. while (bytesLeft < size)
  10. Pthread_cond_wait(&c, &m);
  11. void *ptr = ...; // get mem from heap
  12. bytesLeft -= size;
  13. Pthread_mutex_unlock(&m);
  14. return ptr;
  15. }
  16. void free(void *ptr, int size) {
  17. Pthread_mutex_lock(&m);
  18. bytesLeft += size;
  19. Pthread_cond_signal(&c); // whom to signal??
  20. Pthread_mutex_unlock(&m);
  21. }

Figure 30.13: Covering Conditions: An Example

30.4 总结