# 多线程编程：复习题

> 原文：<https://github.com/angrave/SystemProgramming/wiki/Multi-threaded-Programming%3A-Review-Questions>

> 警告 - 问题编号可能会有变化

## Q1

以下代码是否是线程安全的？重新设计以下代码是线程安全的。提示：如果消息内存对每个调用都是唯一的，则不需要互斥锁。

```c
static char message[20];
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void format(int v) {
  pthread_mutex_lock(&mutex);
  sprintf(message, ":%d:" ,v);
  pthread_mutex_unlock(&mutex);
  return message;
}
```

## Q2

以下哪一项不会导致进程退出？

*   从最后一个运行线程中的 pthread 的启动函数返回。
*   从 main 返回的原始线程。
*   任何导致分段错误的线程。
*   任何调用`exit`的线程。
*   在主线程中调用`pthread_exit`，其他线程仍在运行。

## Q3

写下将由以下程序打印的“W”字符数的数学表达式。假设 a，b，c，d 是小的正整数。你的答案可能会使用'min'函数返回其最低值的参数。

```c
unsigned int a=...,b=...,c=...,d=...;

void* func(void* ptr) {
  char m = * (char*)ptr;
  if(m == 'P') sem_post(s);
  if(m == 'W') sem_wait(s);
  putchar(m);
  return NULL;
}

int main(int argv, char** argc) {
  sem_init(s,0, a);
  while(b--) pthread_create(&tid, NULL, func, "W"); 
  while(c--) pthread_create(&tid, NULL, func, "P"); 
  while(d--) pthread_create(&tid, NULL, func, "W"); 
  pthread_exit(NULL); 
  /*Process will finish when all threads have exited */
}
```

## Q4

完成以下代码。以下代码应该打印交替`A`和`B`。它代表两个轮流执行的线程。将条件变量调用添加到`func`，以便等待的线程不需要连续检查`turn`变量。问：是否需要 pthread_cond_broadcast 或者 pthread_cond_signal 是否足够？

```c
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;

void* turn;

void* func(void* mesg) {
  while(1) {
// Add mutex lock and condition variable calls ...

    while(turn == mesg) { 
        /* poll again ... Change me - This busy loop burns CPU time! */ 
    }

    /* Do stuff on this thread */
    puts( (char*) mesg);
    turn = mesg;

  }
  return 0;
}

int main(int argc, char** argv){
  pthread_t tid1;
  pthread_create(&tid1, NULL, func, "A");
  func("B"); // no need to create another thread - just use the main thread
  return 0;
}
```

## Q5

确定给定代码中的关键部分。添加互斥锁定以使代码线程安全。添加条件变量调用，以使`total`永远不会变为负数或高于 1000.相反，调用应该阻塞，直到可以继续进行。解释为什么`pthread_cond_broadcast`是必要的。

```c
int total;
void add(int value) {
 if(value < 1) return;
 total += value;
}
void sub(int value) {
 if(value < 1) return;
 total -= value;
}
```

## Q6

非线程安全数据结构具有`size()` `enq`和`deq`方法。使用条件变量和互斥锁来完成线程安全的阻塞版本。

```c
void enqueue(void* data) {
  // should block if the size() would become greater than 256
  enq(data);
}
void* dequeue() {
  // should block if size() is 0
  return deq();
}
```

## Q7

您的启动使用最新的交通信息提供路径规划。您的多付实习生创建了一个非线程安全的数据结构，其中包含两个函数：`shortest`（使用但不修改图形）和`set_edge`（修改图形）。

```c
graph_t* create_graph(char* filename); // called once

// returns a new heap object that is the shortest path from vertex i to j
path_t* shortest(graph_t* graph, int i, int j); 

// updates edge from vertex i to j
void set_edge(graph_t* graph, int i, int j, double time); 

```

为了提高性能，多个线程必须能够同时调用`shortest`，但是当`shortest`或`set_edge`内​​没有其他线程执行时，只能通过一个线程修改图形。

使用互斥锁和条件变量来实现读写器解决方案。不完整的尝试如下所示。虽然这种尝试是线程安全的（因此足以用于演示日！），但它不允许多个线程同时计算`shortest`路径并且没有足够的吞吐量。

```c
path_t* shortest_safe(graph_t* graph, int i, int j) {
  pthread_mutex_lock(&m);
  path_t* path = shortest(graph, i, j);
  pthread_mutex_unlock(&m);
  return path;
}
void set_edge_safe(graph_t* graph, int i, int j, double dist) {
  pthread_mutex_lock(&m);
  set_edge(graph, i, j, dist);
  pthread_mutex_unlock(&m);
}
```