————————————————————————
进程的同步、互斥
通过 信号灯集(semaphore)实现。
信号灯集 —— 实现同步
待完善。
信号灯集 —— 实现互斥
待完善。
💡练习:打印 AB
💡同步实现
// prog_syn.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define KEY 20230804
void print_char(char ch) {
printf("%c ", ch);
fflush(stdout);
}
// P操作,申请资源
void semaphore_p(int sem_id, int sem_num) {
struct sembuf sem_buf = {sem_num, -1, 0};
semop(sem_id, &sem_buf, 1);
}
// V操作,释放资源
void semaphore_v(int sem_id, int sem_num) {
struct sembuf sem_buf = {sem_num, 1, 0};
semop(sem_id, &sem_buf, 1);
}
int main(int argc, char const *argv[]) {
// 创建信号量集
int sem_id = semget(KEY, 2, IPC_CREAT | 0666);
if (sem_id == -1) {
perror("Failed to semget");
return -1;
}
// 初始化信号量集
union semun {
int val;
struct semid_ds *buf;
ushort *array;
} arg;
arg.array = (ushort *)malloc(sizeof(ushort) * 2);
arg.array[0] = 1; // 初始化信号量0为1,表示A进程可以打印
arg.array[1] = 0; // 初始化信号量1为0,表示B进程等待
if (semctl(sem_id, 0, SETALL, arg) == -1) {
perror("Failed to semctl");
return -1;
}
pid_t pid = fork();
if (pid < 0) {
perror("Failed to fork");
return -1;
} else if (pid == 0) {
// 子进程,负责打印字符B
while (1) {
semaphore_p(sem_id, 1); // 等待信号量1
print_char('B');
semaphore_v(sem_id, 0); // 发送信号量0
}
} else {
// 父进程,负责打印字符A
while (1) {
sleep(1);
semaphore_p(sem_id, 0); // 等待信号量0
print_char('A');
semaphore_v(sem_id, 1); // 发送信号量1
}
}
// 销毁信号量集
if (semctl(sem_id, 0, IPC_RMID) == -1) {
perror("Failed to semctl");
return -1;
}
return 0;
}
运行结果如下:
💡互斥实现
// prog_mut.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <sys/wait.h>
// 定义信号灯集的键值
#define SEM_KEY 8041942
// 定义信号量操作结构体
struct sembuf sem_operation;
void print_char(char ch) {
printf("%c ", ch);
fflush(stdout);
}
// P操作,申请资源
void semaphore_p(int sem_id, int sem_num) {
struct sembuf sem_buf = {sem_num, -1, 0};
semop(sem_id, &sem_buf, 1);
}
// V操作,释放资源
void semaphore_v(int sem_id, int sem_num) {
struct sembuf sem_buf = {sem_num, 1, 0};
semop(sem_id, &sem_buf, 1);
}
int main(int argc, char const *argv[]) {
// 创建信号灯集
int sem_id = semget(SEM_KEY, 1, IPC_CREAT | 0666);
if (sem_id == -1) {
perror("Failed to create semaphore set");
return 1;
}
// 初始化信号量集
union semun {
int val;
struct semid_ds *buf;
ushort *array;
} arg;
arg.array = (ushort *)malloc(sizeof(ushort) * 1);
arg.array[0] = 1; // 初始化信号量0为1,A\B进程抢夺资源
// 初始化信号量为1,表示可用
if (semctl(sem_id, 0, SETVAL, 1) == -1) {
perror("Failed to initialize semaphore");
return 1;
}
// 创建子进程
pid_t pid = fork();
if (pid == -1) {
perror("Failed to fork");
return 1;
}
if (pid == 0) {
// 子进程
for (int i = 0; i < 10; i++) {
// sleep(1); // 情况二:此句 sleep 被注释掉,下句 sleep 取消注释
// 进入临界区前等待信号量可用
semaphore_p(sem_id, 0);
// 进入临界区,打印字母B
print_char('B');
// 释放信号量,表示临界区可用
semaphore_v(sem_id, 0);
}
} else {
// 父进程
for (int i = 0; i < 10; i++) {
// sleep(1); // 情况一:此句 sleep 被注释掉,上句 sleep 取消注释
// 进入临界区前等待信号量可用
semaphore_p(sem_id, 0);
// 进入临界区,打印字母A
print_char('A');
// 释放信号量,表示临界区可用
semaphore_v(sem_id, 0);
}
wait(NULL);
}
putchar(10);
// 删除信号灯集
// if (semctl(sem_id, 0, IPC_RMID) == -1) {
// perror("Failed to remove semaphore set");
// return -1;
// }
return 0;
}
运行结果如下:
————————————————————————
线程的同步、互斥
通过 信号量 可以实现线程的同步和互斥;
通过 互斥锁 可以实现线程的互斥;
通过 互斥锁 + 条件变量 可以实现线程的同步。
信号量 —— 实现同步
sem_t sem1, sem2, …, sem_n;
sem_init(&sem1, 0, 1);
sem_init(&sem2, 0, 0);
…
sem_init(&sem_n, 0, 0);*thread_1{
sem_wait(&sem1);
// 临界区
sem_post(&sem2);
}
*thread_2{
sem_wait(&sem2);
// 临界区
sem_post(&sem3);
}
…
*thread_n{
sem_wait(&sem_n);
// 临界区
sem_post(&sem_1);
}
信号量 —— 实现互斥
sem_t sem;
sem_init(&sem, 0, 1);
*thread_1{
sem_wait(&sem);
// 临界区
sem_post(&sem);
}
*thread_2{
sem_wait(&sem);
// 临界区
sem_post(&sem);
}
…
*thread_n{
sem_wait(&sem);
// 临界区
sem_post(&sem);
}
💡练习:字符串正序、倒序输出
💡同步实现
// sem_syn.c
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
char buf[64] = "";
sem_t sem1, sem2;
void *reverse(void *arg){
while (1){
char *p = buf;
char *head = p;
char *tail = NULL;
while (*(p+1) != '\0')
p++;
tail = p;
sem_wait(&sem1);
while (head < tail){
*head ^= *tail;
*tail ^= *head;
*head++ ^= *tail--;
}
sem_post(&sem2);
}
pthread_exit(0);
}
void *output(void *arg){
while (1){
sleep(1); // 只加在一个线程函数内即可
sem_wait(&sem2);
printf("%s\n", buf);
sem_post(&sem1);
}
pthread_exit(0);
}
int main(int argc, char const *argv[])
{
sem_init(&sem1, 0, 1);
sem_init(&sem2, 0, 0);
strcpy(buf, "Welcome to my world!");
pthread_t tid1, tid2;
if (pthread_create(&tid1, NULL, output, NULL) != 0){
perror("Failed to create a thread");
return -1;
}
if (pthread_create(&tid2, NULL, reverse, NULL) != 0){
perror("Failed to create a thread");
return -1;
}
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}
运行结果如下:
💡互斥实现(无规律)
// sem_mut.c
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
char buf[64] = "";
sem_t sem;
void *reverse(void *arg){
while (1){
char *p = buf;
char *head = p;
char *tail = NULL;
while (*(p+1) != '\0')
p++;
tail = p;
sem_wait(&sem);
while (head < tail){
*head ^= *tail;
*tail ^= *head;
*head++ ^= *tail--;
}
sem_post(&sem);
}
pthread_exit(0);
}
void *output(void *arg){
while (1){
sleep(1); // 只加在一个线程函数内即可
sem_wait(&sem);
printf("%s\n", buf);
sem_post(&sem);
}
pthread_exit(0);
}
int main(int argc, char const *argv[])
{
sem_init(&sem, 0, 1);
strcpy(buf, "Welcome to my world!");
pthread_t tid1, tid2;
if (pthread_create(&tid1, NULL, output, NULL) != 0){
perror("Failed to create a thread");
return -1;
}
if (pthread_create(&tid2, NULL, reverse, NULL) != 0){
perror("Failed to create a thread");
return -1;
}
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}
运行结果如下:
— — — — — — — — — — — — — — — — — — — — — — — — — — —
互斥锁 —— 实现互斥
pthread_mutex_t lock;
*thread_1{
pthread_mutex_lock(&lock);
// 临界区
pthread_mutex_unlock(&lock);
}
*thread_2{
pthread_mutex_lock(&lock);
// 临界区
pthread_mutex_unlock(&lock);
}
…
*thread_n{
pthread_mutex_lock(&lock);
// 临界区
pthread_mutex_unlock(&lock);
}
互斥锁 + 条件变量 —— 实现同步
双线程:可以先让其中一个线程 sleep,以确保另一个线程先抢到时间片。
多线程:定义 flag = 0; 在每个线程中修改 flag 的值。pthread_cond_broadcast(&condition);
pthread_mutex_t lock;
pthread_cond_t condition;
int flag = 0;*thread_1{
pthread_mutex_lock(&lock);
while(flag != 0)
pthread_cond_wait(&condition, &lock);
// 临界区
flag = 1;
pthread_cond_broadcast(&condition);
pthread_mutex_unlock(&lock);
}
*thread_2{
pthread_mutex_lock(&lock);
while(flag != 1)
pthread_cond_wait(&condition, &lock);
// 临界区
flag = 2;
pthread_cond_broadcast(&condition);
pthread_mutex_unlock(&lock);
}
…
*thread_n{
pthread_mutex_lock(&lock);
while(flag != n-1)
pthread_cond_wait(&condition, &lock);
// 临界区
flag = n;
pthread_cond_broadcast(&condition);
pthread_mutex_unlock(&lock);
}
💡练习:打印 ABC
💡互斥实现(乱序):
// mutex_mut.c
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
pthread_mutex_t lock;
void *print1(void *arg){
for (int i = 1; i <= 6; i++){
pthread_mutex_lock(&lock);
printf("%d: A\t", i);
pthread_mutex_unlock(&lock);
}
pthread_exit(0);
}
void *print2(void *arg){
for (int i = 1; i <= 6; i++){
sleep(1);
pthread_mutex_lock(&lock);
printf("%d: B\t", i);
pthread_mutex_unlock(&lock);
}
pthread_exit(0);
}
void *print3(void *arg){
for (int i = 1; i <= 6; i++){
sleep(2);
pthread_mutex_lock(&lock);
printf("%d: C\t", i);
putchar(10);
pthread_mutex_unlock(&lock);
}
pthread_exit(0);
}
int main(int argc, char const *argv[])
{
pthread_t tid1, tid2, tid3;
if (pthread_create(&tid1, NULL, print1, NULL)){
perror("Failed: ");
return -1;
}
if (pthread_create(&tid2, NULL, print2, NULL)){
perror("Failed: ");
return -1;
}
if (pthread_create(&tid3, NULL, print3, NULL)){
perror("Failed: ");
return -1;
}
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
pthread_mutex_destroy(&lock);
return 0;
}
运行结果如下:
💡同步实现:
// mutex_syn.c
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
pthread_mutex_t lock;
pthread_cond_t condition;
int flag = 0;
void *print1(void *arg){
for (int i = 1; ; i++){
pthread_mutex_lock(&lock);
while(flag != 0)
pthread_cond_wait(&condition, &lock);
printf("%d: A\t", i);
flag = 1;
pthread_cond_broadcast(&condition);
pthread_mutex_unlock(&lock);
}
pthread_exit(0);
}
void *print2(void *arg){
for (int i = 1; ; i++){
pthread_mutex_lock(&lock);
while(flag != 1)
pthread_cond_wait(&condition, &lock);
printf("%d: B\t", i);
flag = 2;
pthread_cond_broadcast(&condition);
pthread_mutex_unlock(&lock);
}
pthread_exit(0);
}
void *print3(void *arg){
for (int i = 1; ; i++){
sleep(1); // 只加在一个线程函数内即可
pthread_mutex_lock(&lock);
while(flag != 2)
pthread_cond_wait(&condition, &lock);
printf("%d: C\t", i);
putchar(10);
flag = 0;
pthread_cond_broadcast(&condition);
pthread_mutex_unlock(&lock);
}
pthread_exit(0);
}
int main(int argc, char const *argv[])
{
pthread_t tid1, tid2, tid3;
if (pthread_create(&tid1, NULL, print1, NULL)){
perror("Failed: ");
return -1;
}
if (pthread_create(&tid2, NULL, print2, NULL)){
perror("Failed: ");
return -1;
}
if (pthread_create(&tid3, NULL, print3, NULL)){
perror("Failed: ");
return -1;
}
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&condition);
return 0;
}
运行结果如下: