# 多线程编程:复习题
> 原文:<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);
}
```
- UIUC CS241 系统编程中文讲义
- 0. 简介
- #Informal 词汇表
- #Piazza:何时以及如何寻求帮助
- 编程技巧,第 1 部分
- 系统编程短篇小说和歌曲
- 1.学习 C
- C 编程,第 1 部分:简介
- C 编程,第 2 部分:文本输入和输出
- C 编程,第 3 部分:常见问题
- C 编程,第 4 部分:字符串和结构
- C 编程,第 5 部分:调试
- C 编程,复习题
- 2.进程
- 进程,第 1 部分:简介
- 分叉,第 1 部分:简介
- 分叉,第 2 部分:Fork,Exec,等等
- 进程控制,第 1 部分:使用信号等待宏
- 进程复习题
- 3.内存和分配器
- 内存,第 1 部分:堆内存简介
- 内存,第 2 部分:实现内存分配器
- 内存,第 3 部分:粉碎堆栈示例
- 内存复习题
- 4.介绍 Pthreads
- Pthreads,第 1 部分:简介
- Pthreads,第 2 部分:实践中的用法
- Pthreads,第 3 部分:并行问题(奖金)
- Pthread 复习题
- 5.同步
- 同步,第 1 部分:互斥锁
- 同步,第 2 部分:计算信号量
- 同步,第 3 部分:使用互斥锁和信号量
- 同步,第 4 部分:临界区问题
- 同步,第 5 部分:条件变量
- 同步,第 6 部分:实现障碍
- 同步,第 7 部分:读者编写器问题
- 同步,第 8 部分:环形缓冲区示例
- 同步复习题
- 6.死锁
- 死锁,第 1 部分:资源分配图
- 死锁,第 2 部分:死锁条件
- 死锁,第 3 部分:餐饮哲学家
- 死锁复习题
- 7.进程间通信&amp;调度
- 虚拟内存,第 1 部分:虚拟内存简介
- 管道,第 1 部分:管道介绍
- 管道,第 2 部分:管道编程秘密
- 文件,第 1 部分:使用文件
- 调度,第 1 部分:调度过程
- 调度,第 2 部分:调度过程:算法
- IPC 复习题
- 8.网络
- POSIX,第 1 部分:错误处理
- 网络,第 1 部分:简介
- 网络,第 2 部分:使用 getaddrinfo
- 网络,第 3 部分:构建一个简单的 TCP 客户端
- 网络,第 4 部分:构建一个简单的 TCP 服务器
- 网络,第 5 部分:关闭端口,重用端口和其他技巧
- 网络,第 6 部分:创建 UDP 服务器
- 网络,第 7 部分:非阻塞 I O,select()和 epoll
- RPC,第 1 部分:远程过程调用简介
- 网络复习题
- 9.文件系统
- 文件系统,第 1 部分:简介
- 文件系统,第 2 部分:文件是 inode(其他一切只是数据...)
- 文件系统,第 3 部分:权限
- 文件系统,第 4 部分:使用目录
- 文件系统,第 5 部分:虚拟文件系统
- 文件系统,第 6 部分:内存映射文件和共享内存
- 文件系统,第 7 部分:可扩展且可靠的文件系统
- 文件系统,第 8 部分:从 Android 设备中删除预装的恶意软件
- 文件系统,第 9 部分:磁盘块示例
- 文件系统复习题
- 10.信号
- 过程控制,第 1 部分:使用信号等待宏
- 信号,第 2 部分:待处理的信号和信号掩码
- 信号,第 3 部分:提高信号
- 信号,第 4 部分:信号
- 信号复习题
- 考试练习题
- 考试主题
- C 编程:复习题
- 多线程编程:复习题
- 同步概念:复习题
- 记忆:复习题
- 管道:复习题
- 文件系统:复习题
- 网络:复习题
- 信号:复习题
- 系统编程笑话