1.进程与线程:区别、通信方式、同步方式
目录
1. 进程 vs 线程:根本区别
2. 进程间通信(IPC)方式
3. 同步机制
4. 同步机制对比
5. 常见问题
Q1: 进程和线程的根本区别是什么?
Q2: 哪种IPC方式最快?为什么?
Q3: 互斥锁和信号量的区别?
Q4: 为什么条件变量需要配合互斥锁使用?
Q5: 什么是死锁?如何避免?
6. 实际应用建议
总结
1. 进程 vs 线程:根本区别
基本概念
-
进程:程序的一次执行实例,是资源分配的基本单位
-
线程:进程内的一个执行流,是CPU调度的基本单位
内存布局对比
进程内存布局:
+-----------------------+
| 栈区 (Stack) | ← 每个线程有独立栈
+-----------------------+
| ↓ |
| |
| 堆区 (Heap) | ← 共享
+-----------------------+
| ↑ |
| |
+-----------------------+
| 全局/静态数据区 | ← 共享
+-----------------------+
| 代码段 | ← 共享
+-----------------------+线程共享:代码段、数据段、堆、文件描述符、信号处理等
线程独有:栈、寄存器、程序计数器、线程局部存储
详细区别对比表
特性 | 进程 | 线程 |
---|---|---|
资源分配 | 操作系统分配独立资源 | 共享进程资源 |
内存空间 | 独立地址空间 | 共享地址空间 |
创建开销 | 大(需要分配资源) | 小(共享资源) |
上下文切换 | 开销大(需要切换页表等) | 开销小(只需切换寄存器) |
通信方式 | 复杂(IPC机制) | 简单(共享内存即可) |
稳定性 | 一个进程崩溃不影响其他进程 | 一个线程崩溃会导致整个进程崩溃 |
数据共享 | 需要显式IPC机制 | 天然共享进程数据 |
独立性 | 完全独立 | 相互依赖 |
核心总结:
-
进程 是资源 ownership 的单位,为程序运行提供独立的沙盒环境。
-
线程 是执行 schedule 的单位,是CPU真正调度和分派的基本单元,共享进程的资源以实现高效协作。
编程:
1. 创建进程 (使用 fork()
)
fork()
是Unix-like系统中创建新进程的主要方法。它会复制当前进程(父进程),创建一个几乎完全相同的子进程。
#include <iostream>
#include <unistd.h> // for fork(), getpid()
#include <sys/wait.h> // for wait()int main() {pid_t pid = fork(); // 创建子进程if (pid < 0) {// fork失败std::cerr << "Fork failed!" << std::endl;return 1;} else if (pid == 0) {// 这里是子进程的代码std::cout << "Hello from Child Process! My PID is: " << getpid() << std::endl;sleep(2); // 模拟子进程工作std::cout << "Child process exiting." << std::endl;} else {// 这里是父进程的代码 (pid > 0, pid是子进程的ID)std::cout << "Hello from Parent Process! I created a child with PID: " << pid << std::endl;wait(nullptr); // 等待子进程结束std::cout << "Parent process resumed after child exited." << std::endl;}return 0;
}
2. 创建线程 (使用 C++11 std::thread
)
C++11提供了标准的线程库,跨平台且易于使用
#include <iostream>
#include <thread>
#include <chrono>// 线程要执行的函数
void thread_function(int id) {std::cout << "Thread " << id << " is starting..." << std::endl;std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟工作std::cout << "Thread " << id << " is finishing..." << std::endl;
}int main() {std::cout << "Main thread started." << std::endl;// 创建并启动两个线程std::thread t1(thread_function, 1);std::thread t2(thread_function, 2);// 等待线程结束t1.join();t2.join();std::cout << "Main thread finished." << std::endl;return 0;
}
2. 进程间通信(IPC)方式
由于进程地址空间独立,无法直接共享变量,因此需要操作系统提供特殊的机制来通信。
1. 管道 (Pipe)
-
概念:一种半双工的通信方式,数据只能单向流动。通常用于有亲缘关系的进程(如父子进程)。
-
原理:在内存中创建一个内核缓冲区,像一个队列。一个进程写,另一个进程读。
-
类型:
-
匿名管道 (Anonymous Pipe):只能在父子进程间使用。
-
命名管道 (Named Pipe / FIFO):提供了一个路径名与之关联,允许无亲缘关系的进程通信。
-
-
特点:简单,但效率较低,容量有限,且通信是单向的。如果需要双向通信,需要建立两个管道。
2. 消息队列 (Message Queue)
-
概念:存放在内核中的消息链表。进程可以向队列中添加消息(写),或从队列中读取消息(读)。
-
原理:每个消息是一个数据块,具有特定的格式和优先级。进程通过消息队列标识符来访问同一个队列。
-
特点:
-
克服了管道只能承载无格式字节流的缺点。
-
独立于发送和接收进程存在(进程终止后,消息队列及其内容并不会被删除)。
-
避免了同步阻塞问题(可以非阻塞地读写)。
-
3. 共享内存 (Shared Memory)
-
概念:最快的IPC方式。允许多个进程共享同一块物理内存区域。
-
原理:
-
由一个进程创建一块共享内存段。
-
其他进程通过系统调用将其映射 (attach) 到自己的地址空间中。
-
之后,进程就可以像访问普通内存一样读写这块区域,从而直接交换数据。
-
-
特点:
-
极快:数据不需要在内核和用户空间之间来回拷贝。
-
需要同步:因为多个进程同时读写同一块内存,必须配合使用信号量或互斥锁等同步机制来确保数据一致性。
-
其他方式:
-
信号 (Signal):一种异步通信机制,用于通知接收进程某个事件已经发生(如
kill -9
)。携带的信息量少。 -
套接字 (Socket):最通用的通信机制,可用于不同机器上的进程间网络通信,也支持同一台主机上的进程通信。
编程:
2.1 管道(Pipe)
#include <iostream>
#include <unistd.h>
#include <string.h>int main() {int fd[2]; // fd[0]是读端,fd[1]是写端pipe(fd); // 创建匿名管道pid_t pid = fork();if (pid == 0) { // 子进程close(fd[0]); // 关闭子进程不需要的读端const char* msg = "Hello from child via pipe!";write(fd[1], msg, strlen(msg) + 1); // 向管道写入数据close(fd[1]);} else { // 父进程close(fd[1]); // 关闭父进程不需要的写端char buffer[100];read(fd[0], buffer, sizeof(buffer)); // 从管道读取数据std::cout << "Parent received: " << buffer << std::endl;close(fd[0]);wait(nullptr);}return 0;
}
特点:单向通信,有亲缘关系的进程间使用,容量有限
2.2 命名管道(FIFO)
命名管道允许无亲缘关系的进程通信。
fifo_writer.cpp (写端)
#include <iostream>
#include <fcntl.h> // for open
#include <unistd.h> // for write, close
#include <cstring> // for strlen
#include <sys/stat.h> // for mkfifoint main() {const char* fifo_name = "/tmp/my_fifo";// 创建命名管道(如果不存在)mkfifo(fifo_name, 0666); // 权限 0666std::cout << "Opening FIFO for writing...\n";int fd = open(fifo_name, O_WRONLY); // 阻塞,直到有读端打开const char* message = "Hello from Writer Process!";write(fd, message, strlen(message) + 1);std::cout << "Message sent.\n";close(fd);// 通常不删除FIFO,以便多次使用// unlink(fifo_name);return 0;
}
fifo_reader.cpp (读端)
#include <iostream>
#include <fcntl.h>
#include <unistd.h>int main() {const char* fifo_name = "/tmp/my_fifo";char buffer[100];std::cout << "Opening FIFO for reading...\n";int fd = open(fifo_name, O_RDONLY); // 阻塞,直到有写端打开read(fd, buffer, sizeof(buffer));std::cout << "Received: " << buffer << std::endl;close(fd);return 0;
}
编译运行:
g++ fifo_writer.cpp -o writer
g++ fifo_reader.cpp -o reader
# 先在一个终端运行 ./writer (它会阻塞等待读者)
# 在另一个终端运行 ./reader
特点:可用于无亲缘关系的进程,有文件名,持久化
2.3 消息队列(Message Queue)
msg_sender.cpp (发送端)
#include <iostream>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <cstring>// 定义消息结构体
struct message_buffer {long msg_type;char msg_text[100];
};int main() {key_t key = ftok("progfile", 65); // 生成唯一keyint msgid = msgget(key, 0666 | IPC_CREAT); // 创建/获取消息队列message_buffer msg;msg.msg_type = 1; // 消息类型strcpy(msg.msg_text, "Hello from Message Queue!");msgsnd(msgid, &msg, sizeof(msg.msg_text), 0); // 发送消息std::cout << "Message sent: " << msg.msg_text << std::endl;return 0;
}
msg_receiver.cpp (接收端)
#include <iostream>
#include <sys/ipc.h>
#include <sys/msg.h>struct message_buffer {long msg_type;char msg_text[100];
};int main() {key_t key = ftok("progfile", 65);int msgid = msgget(key, 0666);message_buffer msg;msgrcv(msgid, &msg, sizeof(msg.msg_text), 1, 0); // 接收类型为1的消息std::cout << "Message received: " << msg.msg_text << std::endl;// 销毁消息队列msgctl(msgid, IPC_RMID, NULL);return 0;
}
编译运行:
g++ msg_sender.cpp -o sender
g++ msg_receiver.cpp -o receiver
./sender
./receiver
特点:消息格式固定,支持优先级,独立于进程存在
2.4 共享内存 (Shared Memory) + 信号量 (Semaphore)
这是最经典的组合:共享内存负责高速数据传输,信号量负责同步。
shm_writer.cpp (写入端)
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h> // 信号量头文件
#include <cstring>
#include <unistd.h>// 联合体,用于semctl初始化
union semun {int val;struct semid_ds *buf;unsigned short *array;
};int main() {key_t key = ftok("shmfile", 65);int shmid = shmget(key, 1024, 0666 | IPC_CREAT);char* str = (char*)shmat(shmid, (void*)0, 0);// 创建信号量key_t sem_key = ftok("semfile", 66);int semid = semget(sem_key, 1, 0666 | IPC_CREAT);union semun su;su.val = 1; // 信号量初始值为1 (互斥锁)semctl(semid, 0, SETVAL, su);struct sembuf sb = {0, -1, 0}; // P操作semop(semid, &sb, 1); // 加锁// 临界区:写入数据std::cout << "Write Data: ";std::cin.getline(str, 1024);std::cout << "Data written: " << str << std::endl;sb.sem_op = 1; // V操作semop(semid, &sb, 1); // 解锁shmdt(str);return 0;
}
shm_reader.cpp (读取端)
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>union semun {int val;struct semid_ds *buf;unsigned short *array;
};int main() {key_t key = ftok("shmfile", 65);int shmid = shmget(key, 1024, 0666);char* str = (char*)shmat(shmid, (void*)0, 0);key_t sem_key = ftok("semfile", 66);int semid = semget(sem_key, 1, 0666);struct sembuf sb = {0, -1, 0}; // P操作semop(semid, &sb, 1); // 加锁// 临界区:读取数据std::cout << "Data read: " << str << std::endl;sb.sem_op = 1; // V操作semop(semid, &sb, 1); // 解锁shmdt(str);shmctl(shmid, IPC_RMID, NULL); // 销毁共享内存semctl(semid, 0, IPC_RMID); // 销毁信号量return 0;
}
编译运行:
g++ shm_writer.cpp -o shm_writer
g++ shm_reader.cpp -o shm_reader
./shm_writer
# 输入数据后,运行
./shm_reader
特点:最快的IPC方式,需要同步机制配合
2.5 信号 (Signal)
signal_example.cpp (发送信号)
#include <iostream>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>int main() {pid_t my_pid = getpid();std::cout << "My PID is: " << my_pid << std::endl;std::cout << "Sending SIGINT signal to myself in 3 seconds...\n";sleep(3);kill(my_pid, SIGINT); // 向自己发送中断信号std::cout << "This line may not be printed.\n";return 0;
}
signal_handler.cpp (捕获和处理信号)
#include <iostream>
#include <csignal>
#include <unistd.h>// 信号处理函数
void signal_handler(int signum) {std::cout << "\nInterrupt signal (" << signum << ") received.\n";// 进行清理操作exit(signum);
}int main() {// 注册信号SIGINT和信号处理函数signal(SIGINT, signal_handler);while (true) {std::cout << "Going to sleep... (Press Ctrl+C to interrupt)\n";sleep(1);}return 0;
}
编译运行:
g++ signal_handler.cpp -o handler
./handler
# 然后按 Ctrl+C 观察效果
特点:异步通知机制,用于简单事件通知
2.6 套接字 (Socket) - Unix Domain Socket (本地)
socket_server.cpp (服务端)
#include <iostream>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <cstring>int main() {int server_fd, client_fd;struct sockaddr_un server_addr, client_addr;socklen_t client_len = sizeof(client_addr);char buffer[100];// 创建socketserver_fd = socket(AF_UNIX, SOCK_STREAM, 0);// 配置地址server_addr.sun_family = AF_UNIX;strcpy(server_addr.sun_path, "/tmp/demo_socket");unlink(server_addr.sun_path); // 确保文件不存在bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));listen(server_fd, 5); // 开始监听std::cout << "Server waiting for connection...\n";client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);read(client_fd, buffer, sizeof(buffer));std::cout << "Server received: " << buffer << std::endl;const char* reply = "Hello from Server!";write(client_fd, reply, strlen(reply) + 1);close(client_fd);close(server_fd);unlink(server_addr.sun_path);return 0;
}
socket_client.cpp (客户端)
#include <iostream>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <cstring>int main() {int sockfd;struct sockaddr_un server_addr;char buffer[100];sockfd = socket(AF_UNIX, SOCK_STREAM, 0);server_addr.sun_family = AF_UNIX;strcpy(server_addr.sun_path, "/tmp/demo_socket");connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));const char* message = "Hello from Client!";write(sockfd, message, strlen(message) + 1);read(sockfd, buffer, sizeof(buffer));std::cout << "Client received: " << buffer << std::endl;close(sockfd);return 0;
}
编译运行:
g++ socket_server.cpp -o server
g++ socket_client.cpp -o client
# 先运行 ./server (它会等待连接)
# 再另一个终端运行 ./client
特点:最通用的IPC方式,支持网络通信
重要说明
-
错误处理:以上示例省略了错误处理以便突出重点,实际使用时务必检查每个系统调用的返回值!
-
编译:所有示例使用
g++
编译,不需要特殊标志(除了需要-pthread
的线程例子)。 -
运行顺序:注意有些示例需要按特定顺序运行(如先server后client,先writer后reader)。
3. 同步机制
当多个执行流(进程或线程)并发地访问共享资源时,为了防止出现竞态条件 (Race Condition) 和数据不一致,需要进行同步。
1. 互斥锁 (Mutex)
-
概念:像一把钥匙,保护一个共享资源。一次只允许一个线程访问该资源。
-
操作:
-
加锁 (Lock):如果锁已被占用,则当前线程会被阻塞,直到锁被释放。
-
解锁 (Unlock):释放锁,允许其他线程获取它。
-
-
特点:实现简单,是最常用的同步机制。锁的持有者必须负责释放锁,否则会导致死锁。
2. 信号量 (Semaphore)
-
概念:一个计数器,用于控制访问多个共享资源的线程数。是比互斥锁更通用的同步原语。
-
操作:
-
P操作 (Wait):尝试减少信号量的值。如果值大于0,则减少并继续;如果值为0,则线程阻塞,直到值大于0。
-
V操作 (Signal):增加信号量的值,并唤醒一个等待的线程(如果有)。
-
-
类型:
-
二进制信号量:值只有0和1,功能上等价于一个互斥锁。
-
计数信号量:值可以大于1,用于控制对一组 identical 资源的访问(例如,有5个打印机的打印池,信号量初始值为5)。
-
3. 条件变量 (Condition Variable)
-
概念:允许线程在某些条件不满足时主动阻塞自己并释放锁,等待其他线程改变条件后通知它。它总是与一个互斥锁结合使用。
-
操作:
-
wait(cond, mutex)
:线程阻塞自己,并原子性地释放互斥锁mutex
。被唤醒后,它会重新获取mutex
再返回。 -
signal(cond)
:唤醒一个正在wait
的线程。 -
broadcast(cond)
:唤醒所有正在wait
的线程。
-
-
使用场景:非常适合用于生产者-消费者模型。当缓冲区空时,消费者线程等待;生产者生产数据后,通知消费者。反之,当缓冲区满时,生产者等待;消费者消费数据后,通知生产者。
同步方式对比总结:
机制 | 核心思想 | 主要用途 |
---|---|---|
互斥锁 (Mutex) | 独占访问,一次一个。 | 保护临界区,确保对单个共享资源的互斥访问。用于简单的互斥 |
信号量 (Semaphore) | 计数器,控制N个访问。 | 控制对一组数量有限的相同资源的访问。用于管理资源池 |
条件变量 (Condition) | 等待条件成立,与锁配合。 | 用于线程间协调,当某个状态条件不满足时让线程等待。用于复杂的线程状态协调 |
3.1 互斥锁(Mutex)
thread:
#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx; // 全局互斥锁
int shared_counter = 0;void increment_counter(int id) {for (int i = 0; i < 5; ++i) {mtx.lock(); // 上锁// 临界区开始++shared_counter;std::cout << "Thread " << id << " incremented counter to: " << shared_counter << std::endl;// 临界区结束mtx.unlock(); // 解锁std::this_thread::sleep_for(std::chrono::milliseconds(10));}
}int main() {std::thread t1(increment_counter, 1);std::thread t2(increment_counter, 2);t1.join();t2.join();std::cout << "Final counter value: " << shared_counter << std::endl;return 0;
}
pthread:
#include <pthread.h>pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int shared_data = 0;void* thread_func(void* arg) {pthread_mutex_lock(&mutex); // 加锁shared_data++; // 临界区pthread_mutex_unlock(&mutex); // 解锁return NULL;
}void mutex_example() {pthread_t t1, t2;pthread_create(&t1, NULL, thread_func, NULL);pthread_create(&t2, NULL, thread_func, NULL);pthread_join(t1, NULL);pthread_join(t2, NULL);printf("Final value: %d\n", shared_data); // 应该是2
}
特点:最简单的同步机制,保证互斥访问
3.2 信号量(Semaphore)
pthread:
#include <semaphore.h>sem_t semaphore;
int buffer[10];
int count = 0;void* producer(void* arg) {for (int i = 0; i < 10; i++) {// 生产数据buffer[count++] = i;sem_post(&semaphore); // V操作,信号量+1}return NULL;
}void* consumer(void* arg) {for (int i = 0; i < 10; i++) {sem_wait(&semaphore); // P操作,信号量-1(如果为0则阻塞)// 消费数据printf("Consumed: %d\n", buffer[--count]);}return NULL;
}void semaphore_example() {sem_init(&semaphore, 0, 0); // 初始值为0pthread_t prod, cons;pthread_create(&prod, NULL, producer, NULL);pthread_create(&cons, NULL, consumer, NULL);pthread_join(prod, NULL);pthread_join(cons, NULL);sem_destroy(&semaphore);
}
C++20 引入了 std::counting_semaphore
。
thread:
#include <iostream>
#include <thread>
#include <semaphore.h> // 注意:这是POSIX信号量,C++20标准信号量用法略有不同// 使用POSIX信号量(更通用)
sem_t sem;void worker(int id) {sem_wait(&sem); // P操作,获取资源std::cout << "Thread " << id << " is in critical section." << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "Thread " << id << " is leaving critical section." << std::endl;sem_post(&sem); // V操作,释放资源
}int main() {// 初始化信号量,初始值为2(允许2个线程同时进入)sem_init(&sem, 0, 2);std::thread t1(worker, 1);std::thread t2(worker, 2);std::thread t3(worker, 3);t1.join();t2.join();t3.join();sem_destroy(&sem); // 销毁信号量return 0;
}
特点:更灵活的同步机制,可以控制多个线程的访问
3.3 条件变量(Condition Variable)
#include <pthread.h>pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int data_ready = 0;void* producer(void* arg) {pthread_mutex_lock(&mutex);// 生产数据data_ready = 1;pthread_cond_signal(&cond); // 通知消费者pthread_mutex_unlock(&mutex);return NULL;
}void* consumer(void* arg) {pthread_mutex_lock(&mutex);while (!data_ready) {pthread_cond_wait(&cond, &mutex); // 等待条件,释放锁}// 消费数据printf("Data is ready!\n");pthread_mutex_unlock(&mutex);return NULL;
}void condition_variable_example() {pthread_t prod, cons;pthread_create(&cons, NULL, consumer, NULL);sleep(1); // 确保消费者先等待pthread_create(&prod, NULL, producer, NULL);pthread_join(prod, NULL);pthread_join(cons, NULL);
}
条件变量 (Condition Variable) - 用于线程间 (生产者-消费者模型)
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>std::mutex mtx;
std::condition_variable cv;
std::queue<int> msgQueue; // 共享的消息队列void producer(int id) {for (int i = 0; i < 5; ++i) {std::unique_lock<std::mutex> lock(mtx);msgQueue.push(i);std::cout << "Producer " << id << " produced: " << i << std::endl;lock.unlock();cv.notify_one(); // 通知一个等待的消费者std::this_thread::sleep_for(std::chrono::milliseconds(100));}
}void consumer(int id) {while (true) {std::unique_lock<std::mutex> lock(mtx);// 等待条件成立:队列非空cv.wait(lock, []{ return !msgQueue.empty(); });int msg = msgQueue.front();msgQueue.pop();std::cout << "Consumer " << id << " consumed: " << msg << std::endl;lock.unlock();if (msg == 4) break; // 简单设定消费到4就退出}
}int main() {std::thread p1(producer, 1);std::thread c1(consumer, 1);p1.join();c1.join();return 0;
}
特点:用于线程间的条件等待和通知,必须与互斥锁配合使用
3.4 读写锁(Read-Write Lock)
#include <pthread.h>pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
int shared_data = 0;void* reader(void* arg) {pthread_rwlock_rdlock(&rwlock); // 读锁printf("Reader: %d\n", shared_data);pthread_rwlock_unlock(&rwlock);return NULL;
}void* writer(void* arg) {pthread_rwlock_wrlock(&rwlock); // 写锁shared_data++;printf("Writer updated to: %d\n", shared_data);pthread_rwlock_unlock(&rwlock);return NULL;
}void rwlock_example() {pthread_t readers[3], writers[2];for (int i = 0; i < 2; i++) {pthread_create(&writers[i], NULL, writer, NULL);}for (int i = 0; i < 3; i++) {pthread_create(&readers[i], NULL, reader, NULL);}// 等待所有线程完成for (int i = 0; i < 2; i++) pthread_join(writers[i], NULL);for (int i = 0; i < 3; i++) pthread_join(readers[i], NULL);
}
特点:允许多个读或一个写,提高读多写少场景的性能
4. 同步机制对比
机制 | 用途 | 特点 | 适用场景 |
---|---|---|---|
互斥锁 | 互斥访问 | 简单,开销小 | 简单的临界区保护 |
信号量 | 资源计数 | 灵活,可控制多个访问 | 生产者-消费者,资源池 |
条件变量 | 条件等待 | 必须配合互斥锁使用 | 复杂的条件同步 |
读写锁 | 读写分离 | 读并发高,写互斥 | 读多写少的场景 |
5. 常见问题
Q1: 进程和线程的根本区别是什么?
答:根本区别在于资源分配。进程是资源分配的基本单位,拥有独立的地址空间;线程是CPU调度的基本单位,共享进程资源。
Q2: 哪种IPC方式最快?为什么?
答:共享内存最快,因为它直接在内存中操作,避免了数据拷贝和系统调用开销。
Q3: 互斥锁和信号量的区别?
答:互斥锁用于互斥访问(二进制信号量),信号量可以用于计数和更复杂的同步场景。
Q4: 为什么条件变量需要配合互斥锁使用?
答:条件变量的等待操作需要原子地释放锁并进入等待状态,唤醒后需要重新获取锁,这需要互斥锁的配合。
Q5: 什么是死锁?如何避免?
答:死锁是多个进程/线程互相等待对方释放资源。避免方法:按固定顺序获取锁、使用超时机制、避免嵌套锁、使用死锁检测算法。
6. 实际应用建议
选择正确的IPC方式
-
高性能:共享内存 + 信号量
-
简单通信:管道或消息队列
-
网络通信:套接字
-
事件通知:信号
同步最佳实践
// 使用RAII模式管理锁
void critical_section() {pthread_mutex_lock(&mutex);// 临界区操作pthread_mutex_unlock(&mutex); // 容易忘记!
}// 更好的方式:使用锁守卫
struct MutexGuard {pthread_mutex_t* mutex;MutexGuard(pthread_mutex_t* m) : mutex(m) { pthread_mutex_lock(mutex); }~MutexGuard() { pthread_mutex_unlock(mutex); }
};void safe_critical_section() {MutexGuard guard(&mutex); // 自动加锁// 临界区操作
} // 自动解锁,异常安全
总结
理解进程和线程的区别以及各种通信同步机制,是操作系统和并发编程的基础:
-
✅ 进程线程区别:资源分配 vs CPU调度
-
✅ IPC方式:根据需求选择合适通信机制
-
✅ 同步机制:保证数据一致性和线程安全
-
✅ 实践原则:选择合适工具,避免死锁,保证性能
2.虚拟内存:分页、分段、页面置换算法
//TODO:keep learning.