信号量
- 原理:信号量是一个整型变量,通过计数器来控制对共享资源的访问。当计数器的值大于0时,表示有可用资源,进程可以获取信号量(计数器减1)来访问共享资源;当计数器为0时,表示资源已被占用,进程需要等待。
- 使用方法(以POSIX信号量为例):
#include <semaphore.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#define SHM_SIZE 1024
int main() {
// 创建共享内存对象
int shm_fd = shm_open("/shared_memory", O_CREAT | O_RDWR, 0666);
ftruncate(shm_fd, SHM_SIZE);
char *shm_ptr = (char *)mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
// 创建信号量
sem_t *sem = sem_open("/my_semaphore", O_CREAT, 0666, 1);
// 子进程
if (fork() == 0) {
sem_wait(sem); // 获取信号量
// 访问共享内存
printf("Child process accessing shared memory: %s\n", shm_ptr);
sem_post(sem); // 释放信号量
_exit(0);
} else {
sem_wait(sem);
// 访问共享内存
sprintf(shm_ptr, "Hello from parent");
sem_post(sem);
wait(NULL);
}
sem_close(sem);
sem_unlink("/my_semaphore");
munmap(shm_ptr, SHM_SIZE);
close(shm_fd);
shm_unlink("/shared_memory");
return 0;
}
- 优点:可以允许多个进程同时访问共享资源(通过设置信号量的初始值),适用于复杂的同步场景。
- 缺点:实现相对复杂,可能会出现死锁等问题,如果使用不当,调试困难。
互斥锁
- 原理:互斥锁是一种特殊的二元信号量(初始值为1),它的值只能是0或1。当一个进程获取了互斥锁(值变为0),其他进程就不能再获取,直到该进程释放互斥锁(值变为1)。这保证了同一时间只有一个进程能够访问共享资源。
- 使用方法(以POSIX互斥锁为例):
#include <pthread.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#define SHM_SIZE 1024
int main() {
// 创建共享内存对象
int shm_fd = shm_open("/shared_memory", O_CREAT | O_RDWR, 0666);
ftruncate(shm_fd, SHM_SIZE);
char *shm_ptr = (char *)mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
// 创建互斥锁
pthread_mutex_t *mutex = (pthread_mutex_t *)mmap(0, sizeof(pthread_mutex_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
pthread_mutex_init(mutex, NULL);
// 子进程
if (fork() == 0) {
pthread_mutex_lock(mutex); // 加锁
// 访问共享内存
printf("Child process accessing shared memory: %s\n", shm_ptr);
pthread_mutex_unlock(mutex); // 解锁
_exit(0);
} else {
pthread_mutex_lock(mutex);
// 访问共享内存
sprintf(shm_ptr, "Hello from parent");
pthread_mutex_unlock(mutex);
wait(NULL);
}
pthread_mutex_destroy(mutex);
munmap(mutex, sizeof(pthread_mutex_t));
munmap(shm_ptr, SHM_SIZE);
close(shm_fd);
shm_unlink("/shared_memory");
return 0;
}
- 优点:简单直观,易于理解和使用,能有效避免多个进程同时访问共享资源导致的数据不一致问题。
- 缺点:同一时间只允许一个进程访问共享资源,可能会导致其他进程等待时间过长,影响系统性能,尤其在并发度较高的场景下。
条件变量
- 原理:条件变量通常与互斥锁一起使用。它允许进程在某个条件满足时被唤醒。进程首先获取互斥锁,然后检查条件是否满足,如果不满足,进程将在条件变量上等待,并释放互斥锁。当另一个进程改变了条件并通知条件变量时,等待的进程被唤醒,重新获取互斥锁,然后检查条件。
- 使用方法(以POSIX条件变量为例):
#include <pthread.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#define SHM_SIZE 1024
int main() {
// 创建共享内存对象
int shm_fd = shm_open("/shared_memory", O_CREAT | O_RDWR, 0666);
ftruncate(shm_fd, SHM_SIZE);
char *shm_ptr = (char *)mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
// 创建互斥锁和条件变量
pthread_mutex_t *mutex = (pthread_mutex_t *)mmap(0, sizeof(pthread_mutex_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
pthread_cond_t *cond = (pthread_cond_t *)mmap(0, sizeof(pthread_cond_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
pthread_mutex_init(mutex, NULL);
pthread_cond_init(cond, NULL);
// 子进程
if (fork() == 0) {
pthread_mutex_lock(mutex);
// 等待条件变量
pthread_cond_wait(cond, mutex);
// 访问共享内存
printf("Child process accessing shared memory: %s\n", shm_ptr);
pthread_mutex_unlock(mutex);
_exit(0);
} else {
pthread_mutex_lock(mutex);
// 设置共享内存
sprintf(shm_ptr, "Hello from parent");
// 通知条件变量
pthread_cond_signal(cond);
pthread_mutex_unlock(mutex);
wait(NULL);
}
pthread_cond_destroy(cond);
pthread_mutex_destroy(mutex);
munmap(cond, sizeof(pthread_cond_t));
munmap(mutex, sizeof(pthread_mutex_t));
munmap(shm_ptr, SHM_SIZE);
close(shm_fd);
shm_unlink("/shared_memory");
return 0;
}
- 优点:提供了更灵活的同步机制,适用于需要等待某个条件满足才能继续执行的场景。
- 缺点:使用相对复杂,需要与互斥锁配合使用,并且如果条件变量的通知和等待处理不当,可能会导致死锁或竞态条件。